From 2b37d7ed86926f0c7c865651b4dee29aaae2015b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= <theo.delrieu@tanker.io>
Date: Mon, 18 Jun 2018 10:53:30 +0200
Subject: [PATCH] from_json: add overload for std::unordered_map

Fixes #1133
---
 .../nlohmann/detail/conversions/from_json.hpp | 20 ++++++++
 test/src/unit-conversions.cpp                 | 50 +++++++++++++++++--
 2 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp
index e77ad5d2..7fe19f57 100644
--- a/include/nlohmann/detail/conversions/from_json.hpp
+++ b/include/nlohmann/detail/conversions/from_json.hpp
@@ -9,6 +9,7 @@
 #include <string> // string
 #include <tuple> // tuple, make_tuple
 #include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <unordered_map> // unordered_map
 #include <utility> // pair, declval
 #include <valarray> // valarray
 
@@ -297,6 +298,25 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
     }
 }
 
+template <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
+          typename = enable_if_t<not std::is_constructible<
+                                     typename BasicJsonType::string_t, Key>::value>>
+void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+{
+    if (JSON_UNLIKELY(not j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+    }
+    for (const auto& p : j)
+    {
+        if (JSON_UNLIKELY(not p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
 struct from_json_fn
 {
   private:
diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp
index 79ccf681..0691bbf4 100644
--- a/test/src/unit-conversions.cpp
+++ b/test/src/unit-conversions.cpp
@@ -40,6 +40,14 @@ using nlohmann::json;
 #include <unordered_set>
 #include <valarray>
 
+namespace
+{
+template <typename MapType>
+void map_type_conversion_checks()
+{
+}
+}
+
 TEST_CASE("value conversion")
 {
     SECTION("get an object (explicit)")
@@ -1088,14 +1096,48 @@ TEST_CASE("value conversion")
                 CHECK(m == m2);
 
                 json j7 = {0, 1, 2, 3};
+                json j8 = 2;
                 CHECK_THROWS_AS((j7.get<std::map<int, int>>()), json::type_error&);
-                CHECK_THROWS_WITH((j7.get<std::map<int, int>>()), "[json.exception.type_error.302] type must be array, but is number");
+                CHECK_THROWS_AS((j8.get<std::map<int, int>>()), json::type_error&);
+                CHECK_THROWS_WITH((j7.get<std::map<int, int>>()),
+                                  "[json.exception.type_error.302] type must be array, "
+                                  "but is number");
+                CHECK_THROWS_WITH((j8.get<std::map<int, int>>()),
+                                  "[json.exception.type_error.302] type must be array, "
+                                  "but is number");
 
                 SECTION("superfluous entries")
                 {
-                  json j8 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
-                  m2 = j8.get<std::map<int, int>>();
-                  CHECK(m == m2);
+                    json j9 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
+                    m2 = j9.get<std::map<int, int>>();
+                    CHECK(m == m2);
+                }
+            }
+
+            SECTION("std::unordered_map (array of pairs)")
+            {
+                std::unordered_map<int, int> m{{0, 1}, {1, 2}, {2, 3}};
+                json j6 = m;
+
+                auto m2 = j6.get<std::unordered_map<int, int>>();
+                CHECK(m == m2);
+
+                json j7 = {0, 1, 2, 3};
+                json j8 = 2;
+                CHECK_THROWS_AS((j7.get<std::unordered_map<int, int>>()), json::type_error&);
+                CHECK_THROWS_AS((j8.get<std::unordered_map<int, int>>()), json::type_error&);
+                CHECK_THROWS_WITH((j7.get<std::unordered_map<int, int>>()),
+                                  "[json.exception.type_error.302] type must be array, "
+                                  "but is number");
+                CHECK_THROWS_WITH((j8.get<std::unordered_map<int, int>>()),
+                                  "[json.exception.type_error.302] type must be array, "
+                                  "but is number");
+
+                SECTION("superfluous entries")
+                {
+                    json j9{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
+                    m2 = j9.get<std::unordered_map<int, int>>();
+                    CHECK(m == m2);
                 }
             }