Browse files

Added utility to merge DOM

  • Loading branch information...
1 parent 19e8353 commit 7ac88fe9ac9cadcc1165500eb7b38357031d4276 @martin-seomoz martin-seomoz committed May 12, 2012
Showing with 261 additions and 1 deletion.
  1. +1 −1 Json/JsonDom.h
  2. +100 −0 Json/JsonUtil.cpp
  3. +20 −0 Json/JsonUtil.h
  4. +140 −0 Json/test/JsonUtilTest.cpp
View
2 Json/JsonDom.h
@@ -107,7 +107,7 @@ struct JsonValue
this->setValue(value);
return value;
}
- virtual void print(std::ostream& stream) const = 0;
+ virtual void print(std::ostream& /*stream*/) const {throw std::runtime_error("Invalid Json");}
private:
virtual void setValue(long&) const { throw InvalidConversion();}
View
100 Json/JsonUtil.cpp
@@ -0,0 +1,100 @@
+
+#include "JsonUtil.h"
+#include <sstream>
+
+using namespace ThorsAnvil::Json;
+
+/* Forward declaration of static functions */
+static void mergeJsonDom(JsonMap& dst, JsonMap& src, std::string const& errorMsg, std::string const& index);
+static void mergeJsonDom(JsonArray& dst, JsonArray& src, std::string const& errorMsg, std::string const& index);
+
+
+/* Implementation */
+static void mergeJsonDom(JsonMap& dst, JsonMap& src, std::string const& errorMsg, std::string const& index)
+{
+ for(JsonMap::iterator loop = src.begin(); loop != src.end(); ++loop)
+ {
+ JsonMap::iterator find = dst.find(loop->first);
+ if (find == dst.end())
+ {
+ dst.transfer(loop, src);
+ }
+ else
+ {
+ JsonMapItem* map = dynamic_cast<JsonMapItem*>(find->second);
+ JsonArrayItem* arr = dynamic_cast<JsonArrayItem*>(find->second);
+
+ if ((map == NULL) && (arr == NULL))
+ {
+ // Normal Item (not map or array).
+ // The src item over-rides the destination item
+ dst.erase(find);
+ dst.transfer(loop, src);
+ }
+ else
+ {
+ // The item in the destination is a map or an array.
+ // Check what the value we are merging into this object is
+ JsonMapItem* srcMapItem = dynamic_cast<JsonMapItem*>(loop->second);
+
+ if ((map != NULL) && (srcMapItem == NULL))
+ {
+ // Maps can only be combined with other maps.
+
+ char const* into = (map != NULL) ? "Map" : "Array";
+ char const* from = (srcMapItem != NULL) ? "Map" : "Other";
+ std::stringstream msg;
+ msg << "Invalid Config Merge into " << into << " from " << from << " in file: `" << errorMsg << "` into `" << index << "`:" << find->first;
+ throw std::runtime_error(msg.str());
+ }
+
+ if (map != NULL) // If he destination is a map then the source must be a map
+ {
+ // Recursively combine maps.
+ JsonMap& dstMap = *map->value;
+ JsonMap& srcMap = *srcMapItem->value;
+
+ mergeJsonDom(dstMap, srcMap, errorMsg, index + ":" + find->first);
+ }
+ else if (arr != NULL)
+ {
+ // Combine stuff into an array.
+ JsonArrayItem* srcArrayItem = dynamic_cast<JsonArrayItem*>(loop->second);
+
+ if (srcArrayItem != NULL)
+ {
+ // Two arrays are concatenate together.
+ JsonArray& dstArray = *arr->value;
+ JsonArray& srcArray = *srcArrayItem->value;
+
+ mergeJsonDom(dstArray, srcArray, errorMsg, index);
+ }
+ else
+ {
+ // Normal Items are just append onto the end of the array
+ JsonArray& dstArray = *arr->value;
+ dstArray.push_back(src.release(loop).release());
+ }
+ }
+ }
+ }
+ }
+}
+
+static void mergeJsonDom(JsonArray& dst, JsonArray& src, std::string const& /*errorMsg*/, std::string const& /*index*/)
+{
+ dst.transfer(dst.end(), src.begin(), src.end(), src);
+}
+
+void ThorsAnvil::Json::mergeJsonDom(JsonMap& dst, JsonMap& src, std::string const& errorMsg)
+{
+ ::mergeJsonDom(dst, src, errorMsg, "root");
+}
+
+void ThorsAnvil::Json::mergeJsonDom(JsonArray& dst, JsonArray& src, std::string const& errorMsg)
+{
+ ::mergeJsonDom(dst, src, errorMsg, "root");
+}
+
+
+
View
20 Json/JsonUtil.h
@@ -0,0 +1,20 @@
+
+#ifndef THORSANVIL_JSON_JSON_UTIL_H
+#define THORSANVIL_JSON_JSON_UTIL_H
+
+#include "JsonDom.h"
+
+namespace ThorsAnvil
+{
+ namespace Json
+ {
+
+void mergeJsonDom(JsonMap& dst, JsonMap& src, std::string const& errorMsg);
+void mergeJsonDom(JsonArray& dst, JsonArray& src, std::string const& errorMsg);
+
+ }
+}
+
+
+#endif
+
View
140 Json/test/JsonUtilTest.cpp
@@ -0,0 +1,140 @@
+
+
+#include "gtest/gtest.h"
+#include "JsonUtil.h"
+#include "JsonDom.h"
+#include "ParserShiftReduce.h"
+#include "ScannerDom.h"
+
+using namespace ThorsAnvil::Json;
+
+TEST(JsonUtil, MergeArrays)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("[1, 2, 3, 4]");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonArray> array1 = scanner.getArray();
+
+ std::stringstream data2("[1, 2, 3, 4]");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonArray> array2 = scanner.getArray();
+
+ mergeJsonDom(*array1, *array2, "Test");
+ EXPECT_EQ(8, array1->size());
+}
+
+
+TEST(JsonUtil, MergeMaps)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("{ \"item1\": 1, \"item2\": 2, \"item3\": 3, \"item4\": 4}");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonMap> map1 = scanner.getMap();
+
+ std::stringstream data2("{ \"item5\": 1, \"item6\": 2, \"item7\": 3, \"item8\": 4}");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonMap> map2 = scanner.getMap();
+
+ mergeJsonDom(*map1, *map2, "Test");
+ EXPECT_EQ(8, map1->size());
+}
+
+
+TEST(JsonUtil, OverwiteMapPODElement)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("{ \"item1\": 4}");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonMap> map1 = scanner.getMap();
+
+ std::stringstream data2("{ \"item1\": 5}");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonMap> map2 = scanner.getMap();
+
+ mergeJsonDom(*map1, *map2, "Test");
+ EXPECT_EQ(1, map1->size());
+ EXPECT_EQ(5, (*map1)["item1"].getValue<int>());
+}
+
+
+TEST(JsonUtil, OverwiteMapElement)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("{ \"item1\": {\"t1\": 9}}");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonMap> map1 = scanner.getMap();
+
+ std::stringstream data2("{ \"item1\": {\"t2\": 15}}");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonMap> map2 = scanner.getMap();
+
+ mergeJsonDom(*map1, *map2, "Test");
+ EXPECT_EQ(1, map1->size());
+
+ JsonMap& subMap = *static_cast<JsonMapItem&>((*map1)["item1"]).value;
+ EXPECT_EQ(9, (subMap)["t1"].getValue<int>());
+ EXPECT_EQ(15, (subMap)["t2"].getValue<int>());
+}
+
+
+TEST(JsonUtil, AddElementsToArrayInMap)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("{ \"item1\": [ 1, 2, 3 ]}");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonMap> map1 = scanner.getMap();
+
+ std::stringstream data2("{ \"item1\": {\"item5\": 34}}");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonMap> map2 = scanner.getMap();
+
+ mergeJsonDom(*map1, *map2, "Test");
+ EXPECT_EQ(1, map1->size());
+
+ JsonArray& subArray = *static_cast<JsonArrayItem&>((*map1)["item1"]).value;
+ EXPECT_EQ(4, subArray.size());
+}
+
+
+TEST(JsonUtil, AddArrayToArrayInMap)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("{ \"item1\": [ 1, 2, 3 ]}");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonMap> map1 = scanner.getMap();
+
+ std::stringstream data2("{ \"item1\": [ 4, 5, {\"plop\": 8}]}");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonMap> map2 = scanner.getMap();
+
+ mergeJsonDom(*map1, *map2, "Test");
+ EXPECT_EQ(1, map1->size());
+
+ JsonArray& subArray = *static_cast<JsonArrayItem&>((*map1)["item1"]).value;
+ EXPECT_EQ(6, subArray.size());
+}
+
+
+TEST(JsonUtil, MergeNonMapIntoMap)
+{
+ ScannerDom scanner;
+
+ std::stringstream data1("{ \"item1\": {\"plop\": 1}}");
+ scanner.parse<yy::ParserShiftReduce>(data1);
+ std::auto_ptr<JsonMap> map1 = scanner.getMap();
+
+ std::stringstream data2("{ \"item1\": 12}");
+ scanner.parse<yy::ParserShiftReduce>(data2);
+ std::auto_ptr<JsonMap> map2 = scanner.getMap();
+
+ ASSERT_THROW(mergeJsonDom(*map1, *map2, "Test"), std::runtime_error);
+}
+
+
+

0 comments on commit 7ac88fe

Please sign in to comment.