From feebd980e5623500ed63ed09c967fc8cf7b7ef7d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 2 Nov 2016 21:20:13 +0000 Subject: Support [de]serializing XML elements/attributes to/from a dictionary --- slicer/test/Jamfile.jam | 1 + slicer/test/initial/attributemap.xml | 6 ++ slicer/test/initial/elementmap.xml | 14 +++++ slicer/test/preprocessor.cpp | 2 +- slicer/test/serializers.cpp | 35 +++++++++++ slicer/test/xml.ice | 10 +++ slicer/xml/serializer.cpp | 118 +++++++++++++++++++++++++++++------ slicer/xml/serializer.h | 6 ++ 8 files changed, 173 insertions(+), 19 deletions(-) create mode 100644 slicer/test/initial/attributemap.xml create mode 100644 slicer/test/initial/elementmap.xml diff --git a/slicer/test/Jamfile.jam b/slicer/test/Jamfile.jam index 4046e6f..53a9489 100644 --- a/slicer/test/Jamfile.jam +++ b/slicer/test/Jamfile.jam @@ -64,6 +64,7 @@ run preprocessor.cpp adhocutil .. ../tool//slicer-compiler + types : preprocess ; diff --git a/slicer/test/initial/attributemap.xml b/slicer/test/initial/attributemap.xml new file mode 100644 index 0000000..7651ac6 --- /dev/null +++ b/slicer/test/initial/attributemap.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/slicer/test/initial/elementmap.xml b/slicer/test/initial/elementmap.xml new file mode 100644 index 0000000..1f8d913 --- /dev/null +++ b/slicer/test/initial/elementmap.xml @@ -0,0 +1,14 @@ + + + + + one + two + three + + + one + two + three + + diff --git a/slicer/test/preprocessor.cpp b/slicer/test/preprocessor.cpp index 7431a2d..cd2cd8d 100644 --- a/slicer/test/preprocessor.cpp +++ b/slicer/test/preprocessor.cpp @@ -20,7 +20,7 @@ ComponentsCount COMPONENTS_IN_TEST_ICE = { { "inheritance.ice", 12 }, { "interfaces.ice", 0 }, { "json.ice", 2 }, - { "xml.ice", 2 }, + { "xml.ice", 5 }, { "db.ice", 4 }, { "types.ice", 3 } }; diff --git a/slicer/test/serializers.cpp b/slicer/test/serializers.cpp index 6218b3b..350d570 100644 --- a/slicer/test/serializers.cpp +++ b/slicer/test/serializers.cpp @@ -253,6 +253,31 @@ checkSomeNumbers(const TestModule::SomeNumbers & sn) BOOST_REQUIRE_EQUAL(sn, TestModule::FiftyFive); } +void +attributeMap(const TestXml::Maps & s) +{ + BOOST_REQUIRE_EQUAL(3, s.amap.size()); + BOOST_REQUIRE_EQUAL("one", s.amap.find("a")->second); + BOOST_REQUIRE_EQUAL("two", s.amap.find("b")->second); + BOOST_REQUIRE_EQUAL("three", s.amap.find("c")->second); +} + +void +elementMap(const TestXml::Maps & s) +{ + BOOST_REQUIRE_EQUAL(3, s.emap.size()); + BOOST_REQUIRE_EQUAL("one", s.emap.find("a")->second); + BOOST_REQUIRE_EQUAL("two", s.emap.find("b")->second); + BOOST_REQUIRE_EQUAL("three", s.emap.find("c")->second); + BOOST_REQUIRE_EQUAL(3, s.rmap.size()); + BOOST_REQUIRE_EQUAL(1, s.rmap.find("a")->second.Id); + BOOST_REQUIRE_EQUAL(2, s.rmap.find("b")->second.Id); + BOOST_REQUIRE_EQUAL(3, s.rmap.find("c")->second.Id); + BOOST_REQUIRE_EQUAL("one", s.rmap.find("a")->second.Name); + BOOST_REQUIRE_EQUAL("two", s.rmap.find("b")->second.Name); + BOOST_REQUIRE_EQUAL("three", s.rmap.find("c")->second.Name); +} + void checkObjectMap(const TestJson::Properties & p) { @@ -437,6 +462,16 @@ BOOST_AUTO_TEST_CASE( xml_rootEnums_xml ) verifyByFile("enum.xml", checkSomeNumbers); } +BOOST_AUTO_TEST_CASE( xml_attributemap_xml ) +{ + verifyByFile("attributemap.xml", attributeMap); +} + +BOOST_AUTO_TEST_CASE( xml_elementmap_xml ) +{ + verifyByFile("elementmap.xml", elementMap); +} + BOOST_AUTO_TEST_CASE( json_rootEnums_json ) { verifyByFile("enum2.json", checkSomeNumbers); diff --git a/slicer/test/xml.ice b/slicer/test/xml.ice index ecb50b5..6deb02f 100644 --- a/slicer/test/xml.ice +++ b/slicer/test/xml.ice @@ -16,6 +16,16 @@ module TestXml { [ "slicer:xml:text" ] string Name; }; + dictionary StringMap; + dictionary RefMap; + struct Maps { + [ "slicer:xml:attributes" ] + StringMap amap; + [ "slicer:xml:elements" ] + StringMap emap; + [ "slicer:xml:elements" ] + RefMap rmap; + }; }; #endif diff --git a/slicer/xml/serializer.cpp b/slicer/xml/serializer.cpp index 654793c..307819a 100644 --- a/slicer/xml/serializer.cpp +++ b/slicer/xml/serializer.cpp @@ -19,6 +19,10 @@ namespace Slicer { const std::string md_attribute = "xml:attribute"; const std::string md_text = "xml:text"; const std::string md_bare = "xml:bare"; + const std::string md_attributes = "xml:attributes"; + const std::string md_elements = "xml:elements"; + const std::string keyName = "key"; + const std::string valueName = "value"; const auto defaultElementCreator = boost::bind(&xmlpp::Element::add_child, _1, _2, Glib::ustring()); static const Glib::ustring TrueText("true"); @@ -170,6 +174,75 @@ namespace Slicer { } }; + void + XmlDeserializer::DocumentTreeIterateDictAttrs(const xmlpp::Element::AttributeList & attrs, ModelPartPtr dict) + { + for (const auto & attr : attrs) { + auto emp = dict->GetAnonChild(); + emp->Create(); + auto key = emp->GetChild(keyName); + auto value = emp->GetChild(valueName); + key->SetValue(new XmlValueSource(attr->get_name())); + key->Complete(); + value->SetValue(new XmlValueSource(attr->get_value())); + value->Complete(); + emp->Complete(); + } + } + + void + XmlDeserializer::DocumentTreeIterateDictElements(const xmlpp::Element * element, ModelPartPtr dict) + { + auto node = element->get_first_child(); + while (node) { + if (auto element = dynamic_cast(node)) { + auto emp = dict->GetAnonChild(); + emp->Create(); + auto key = emp->GetChild(keyName); + auto value = emp->GetChildRef(valueName); + key->SetValue(new XmlValueSource(element->get_name())); + key->Complete(); + DocumentTreeIterateElement(element, value->Child(), value); + emp->Complete(); + } + node = node->get_next_sibling(); + } + } + + void + XmlDeserializer::DocumentTreeIterateElement(const xmlpp::Element * element, ModelPartPtr smp, ChildRefPtr smpr) + { + if (auto typeIdPropName = smp->GetTypeIdProperty()) { + if (auto typeAttr = element->get_attribute(*typeIdPropName)) { + smp = smp->GetSubclassModelPart(typeAttr->get_value()); + } + } + smp->Create(); + if (metaDataFlagSet(smpr->ChildMetaData(), md_attributes)) { + auto attrs(element->get_attributes()); + if (!attrs.empty()) { + DocumentTreeIterateDictAttrs(attrs, smp); + } + } + else if (metaDataFlagSet(smpr->ChildMetaData(), md_elements)) { + DocumentTreeIterateDictElements(element, smp); + } + else { + auto attrs(element->get_attributes()); + if (!attrs.empty()) { + DocumentTreeIterate(attrs.front(), smp); + } + auto firstChild = element->get_first_child(); + if (firstChild) { + DocumentTreeIterate(firstChild, smp); + } + else { + smp->SetValue(new XmlContentValueSource()); + } + } + smp->Complete(); + } + void XmlDeserializer::DocumentTreeIterate(const xmlpp::Node * node, ModelPartPtr mp) { @@ -183,24 +256,7 @@ namespace Slicer { smp = smp->GetAnonChild(); } if (smp) { - if (auto typeIdPropName = smp->GetTypeIdProperty()) { - if (auto typeAttr = element->get_attribute(*typeIdPropName)) { - smp = smp->GetSubclassModelPart(typeAttr->get_value()); - } - } - smp->Create(); - auto attrs(element->get_attributes()); - if (!attrs.empty()) { - DocumentTreeIterate(attrs.front(), smp); - } - auto firstChild = element->get_first_child(); - if (firstChild) { - DocumentTreeIterate(firstChild, smp); - } - else { - smp->SetValue(new XmlContentValueSource()); - } - smp->Complete(); + DocumentTreeIterateElement(element, smp, smpr); } } } @@ -247,6 +303,12 @@ namespace Slicer { else if (hp && metaDataFlagSet(hp->GetMetadata(), md_text)) { mp->GetValue(new XmlContentValueTarget(n)); } + else if (hp && metaDataFlagSet(hp->GetMetadata(), md_attributes)) { + ModelTreeIterateDictAttrs(n->add_child(name), mp); + } + else if (hp && metaDataFlagSet(hp->GetMetadata(), md_elements)) { + ModelTreeIterateDictElements(n->add_child(name), mp); + } else { if (hp && metaDataFlagSet(hp->GetMetadata(), md_bare)) { ModelTreeProcessElement(n, mp, boost::bind(&xmlpp::Element::add_child, _1, name, Glib::ustring())); @@ -257,6 +319,26 @@ namespace Slicer { } } + void + XmlSerializer::ModelTreeIterateDictAttrs(xmlpp::Element * element, ModelPartPtr dict) + { + dict->OnEachChild([element](const auto &, const auto & mp, const auto &) { + mp->GetChild(keyName)->GetValue(new XmlValueTarget([&mp,element](const auto & name) { + mp->GetChild(valueName)->GetValue(new XmlAttributeValueTarget(element, name)); + })); + }); + } + + void + XmlSerializer::ModelTreeIterateDictElements(xmlpp::Element * element, ModelPartPtr dict) + { + dict->OnEachChild([element](const auto &, const auto & mp, const auto &) { + mp->GetChild(keyName)->GetValue(new XmlValueTarget([&mp,element](const auto & name) { + ModelTreeProcessElement(element->add_child(name), mp->GetChild(valueName), defaultElementCreator); + })); + }); + } + void XmlSerializer::ModelTreeProcessElement(xmlpp::Element * element, ModelPartPtr mp, const ElementCreator & ec) { diff --git a/slicer/xml/serializer.h b/slicer/xml/serializer.h index 23ba17a..5b56ad1 100644 --- a/slicer/xml/serializer.h +++ b/slicer/xml/serializer.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace Slicer { @@ -14,6 +15,8 @@ namespace Slicer { protected: static void ModelTreeProcessElement(xmlpp::Element * n, ModelPartPtr mp, const ElementCreator &); + static void ModelTreeIterateDictAttrs(xmlpp::Element * element, ModelPartPtr dict); + static void ModelTreeIterateDictElements(xmlpp::Element * element, ModelPartPtr dict); }; class DLL_PUBLIC XmlStreamSerializer : public XmlSerializer { @@ -49,7 +52,10 @@ namespace Slicer { class DLL_PUBLIC XmlDeserializer : public Deserializer { protected: static void DocumentTreeIterate(const xmlpp::Node * node, ModelPartPtr mp); + static void DocumentTreeIterateElement(const xmlpp::Element * element, ModelPartPtr mp, ChildRefPtr c); static void DocumentTreeIterate(const xmlpp::Document * doc, ModelPartPtr mp); + static void DocumentTreeIterateDictAttrs(const xmlpp::Element::AttributeList & attrs, ModelPartPtr dict); + static void DocumentTreeIterateDictElements(const xmlpp::Element * parent, ModelPartPtr dict); }; class DLL_PUBLIC XmlStreamDeserializer : public XmlDeserializer { -- cgit v1.2.3