Merge branch 'develop' of https://github.com/nlohmann/json into feature/optional
diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp
index 438b84a..3300b49 100644
--- a/include/nlohmann/detail/conversions/from_json.hpp
+++ b/include/nlohmann/detail/conversions/from_json.hpp
@@ -32,6 +32,21 @@
     n = nullptr;
 }
 
+#ifdef JSON_HAS_CPP_17
+template<typename BasicJsonType, typename T>
+void from_json(const BasicJsonType& j, std::optional<T>& opt)
+{
+    if (j.is_null())
+    {
+        opt = std::nullopt;
+    }
+    else
+    {
+        opt = j.template get<T>();
+    }
+}
+#endif
+
 // overloads for basic_json template parameters
 template < typename BasicJsonType, typename ArithmeticType,
            enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp
index b45004f..dcee1fd 100644
--- a/include/nlohmann/detail/conversions/to_json.hpp
+++ b/include/nlohmann/detail/conversions/to_json.hpp
@@ -219,6 +219,22 @@
 // to_json //
 /////////////
 
+#ifdef JSON_HAS_CPP_17
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
+void to_json(BasicJsonType& j, const std::optional<T>& opt)
+{
+    if (opt.has_value())
+    {
+        j = *opt;
+    }
+    else
+    {
+        j = nullptr;
+    }
+}
+#endif
+
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
 void to_json(BasicJsonType& j, T b) noexcept
diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp
index 77acf04..412858e 100644
--- a/include/nlohmann/detail/macro_scope.hpp
+++ b/include/nlohmann/detail/macro_scope.hpp
@@ -31,6 +31,10 @@
     #define JSON_HAS_CPP_14
 #endif
 
+#ifdef JSON_HAS_CPP_17
+    #include <optional>
+#endif
+
 // disable float-equal warnings on GCC/clang
 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
     #pragma GCC diagnostic push
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 8b6344f..897d6b6 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -2108,6 +2108,10 @@
     #define JSON_HAS_CPP_14
 #endif
 
+#ifdef JSON_HAS_CPP_17
+    #include <optional>
+#endif
+
 // disable float-equal warnings on GCC/clang
 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
     #pragma GCC diagnostic push
@@ -3518,6 +3522,21 @@
     n = nullptr;
 }
 
+#ifdef JSON_HAS_CPP_17
+template<typename BasicJsonType, typename T>
+void from_json(const BasicJsonType& j, std::optional<T>& opt)
+{
+    if (j.is_null())
+    {
+        opt = std::nullopt;
+    }
+    else
+    {
+        opt = j.template get<T>();
+    }
+}
+#endif
+
 // overloads for basic_json template parameters
 template < typename BasicJsonType, typename ArithmeticType,
            enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
@@ -4294,6 +4313,22 @@
 // to_json //
 /////////////
 
+#ifdef JSON_HAS_CPP_17
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
+void to_json(BasicJsonType& j, const std::optional<T>& opt)
+{
+    if (opt.has_value())
+    {
+        j = *opt;
+    }
+    else
+    {
+        j = nullptr;
+    }
+}
+#endif
+
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
 void to_json(BasicJsonType& j, T b) noexcept
diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp
index 7f59c63..6de003d 100644
--- a/test/src/unit-conversions.cpp
+++ b/test/src/unit-conversions.cpp
@@ -1704,6 +1704,65 @@
 }
 
 #ifdef JSON_HAS_CPP_17
+TEST_CASE("std::optional")
+{
+    SECTION("null")
+    {
+        json j_null;
+        std::optional<std::string> opt_null;
+
+        CHECK(json(opt_null) == j_null);
+        CHECK(std::optional<std::string>(j_null) == std::nullopt);
+    }
+
+    SECTION("string")
+    {
+        json j_string = "string";
+        std::optional<std::string> opt_string = "string";
+
+        CHECK(json(opt_string) == j_string);
+        CHECK(std::optional<std::string>(j_string) == opt_string);
+    }
+
+    SECTION("bool")
+    {
+        json j_bool = true;
+        std::optional<bool> opt_bool = true;
+
+        CHECK(json(opt_bool) == j_bool);
+        CHECK(std::optional<bool>(j_bool) == opt_bool);
+    }
+
+    SECTION("number")
+    {
+        json j_number = 1;
+        std::optional<int> opt_int = 1;
+
+        CHECK(json(opt_int) == j_number);
+        CHECK(std::optional<int>(j_number) == opt_int);
+    }
+
+    SECTION("array")
+    {
+        json j_array = {1, 2, nullptr};
+        std::vector<std::optional<int>> opt_array = {{1, 2, std::nullopt}};
+
+        CHECK(json(opt_array) == j_array);
+        CHECK(std::vector<std::optional<int>>(j_array) == opt_array);
+    }
+
+    SECTION("object")
+    {
+        json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}};
+        std::map<std::string, std::optional<int>> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}};
+
+        CHECK(json(opt_object) == j_object);
+        CHECK(std::map<std::string, std::optional<int>>(j_object) == opt_object);
+    }
+}
+#endif
+
+#ifdef JSON_HAS_CPP_17
     #undef JSON_HAS_CPP_17
 #endif