From 255362d5f815ca0e04e18518bb3629d241af5117 Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Sun, 22 Feb 2015 11:48:19 +0100 Subject: corrected XML serialization for primitive text content regarding whitespace and newline handling and extended XMLTest to check that. --- src/core/XML.cpp | 114 ++++++++++++++++++++++++++++++++++++++------------ test/core/XMLTest.cpp | 22 ++++------ 2 files changed, 94 insertions(+), 42 deletions(-) diff --git a/src/core/XML.cpp b/src/core/XML.cpp index e25d18a..404af92 100644 --- a/src/core/XML.cpp +++ b/src/core/XML.cpp @@ -66,13 +66,82 @@ static std::string escapePredefinedEntities(const std::string &input) void Element::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty) { + bool hasText = false; if (pretty) { + // print tabs at the beginning, if we are in pretty mode. for (unsigned int t = 0; t < tabdepth; t++) { out << '\t'; } + /* + * if we are in pretty mode we also need to check if we have a text + * node as child. + * If so this changes our further output processing because of the way + * XML treats primitive data: The structure + * + * \code{.xml} + * + * content + * content2 + * + * \endcode + * + * has to be serialized as + * + * \code{.xml} + * contentcontent2 + * \endcode + * + * because otherwise we introduce whitespaces and newlines where no + * such things had been before. + * + * On the other hand the structure + * + * \code{.xml} + * + * + * content + * + * + * \endcode + * + * Can be serialized as + * + * \code{.xml} + * + * content + * + * \endcode + * + * As last example consider the case + * + * \code{.xml} + * + * + * content + * + * content2 + * + * \endcode + * + * Here the A-Element again has primitive text content, such that we + * are not allowed to prettify. It has to be serialized like this: + * + * \code{.xml} + * contentcontent2 + * \endcode + * + * + */ + for (auto n : children) { + if (n->isa(&RttiTypes::XMLText)) { + hasText = true; + break; + } + } } + out << '<'; - if(!nspace.empty()){ + if (!nspace.empty()) { out << nspace << ":"; } out << name; @@ -88,19 +157,19 @@ void Element::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty) } } else { out << ">"; - if (pretty) { + if (pretty && !hasText) { out << std::endl; } - for (auto &n : children) { - n->doSerialize(out, tabdepth + 1, pretty); + for (auto n : children) { + n->doSerialize(out, tabdepth + 1, pretty && !hasText); } - if (pretty) { + if (pretty && !hasText) { for (unsigned int t = 0; t < tabdepth; t++) { out << '\t'; } } out << ""; @@ -112,29 +181,20 @@ void Element::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty) void Text::doSerialize(std::ostream &out, unsigned int tabdepth, bool pretty) { - if (pretty) { - for (unsigned int t = 0; t < tabdepth; t++) { - out << '\t'; - } - } out << escapePredefinedEntities(text); - if (pretty) { - out << std::endl; - } } } -namespace RttiTypes -{ - const Rtti XMLNode = RttiBuilder("XMLNode"); - const Rtti XMLElement = - RttiBuilder("XMLElement") - .parent(&XMLNode) - .composedOf(&XMLNode) - .property("name", {&RttiTypes::String, - {[](const xml::Element *obj) { - return Variant::fromString(obj->getName()); - }}}); - const Rtti XMLText = RttiBuilder("XMLText").parent(&XMLNode); -} +namespace RttiTypes { +const Rtti XMLNode = RttiBuilder("XMLNode"); +const Rtti XMLElement = + RttiBuilder("XMLElement") + .parent(&XMLNode) + .composedOf(&XMLNode) + .property("name", {&RttiTypes::String, + {[](const xml::Element *obj) { + return Variant::fromString(obj->getName()); + }}}); +const Rtti XMLText = RttiBuilder("XMLText").parent(&XMLNode); } +} \ No newline at end of file diff --git a/test/core/XMLTest.cpp b/test/core/XMLTest.cpp index b11a24c..c264570 100644 --- a/test/core/XMLTest.cpp +++ b/test/core/XMLTest.cpp @@ -42,29 +42,21 @@ TEST(XMLNode, testSerialize) body->addChild(div); Rooted p{new Element{mgr, div, "p"}}; div->addChild(p); - p->addChild(new Text(mgr, p, "my text")); - Rooted p2{new Element{mgr, div, "p"}}; - div->addChild(p2); - p2->addChild(new Text(mgr, p2, "my text")); + p->addChild(new Text(mgr, p, "A")); + div->addChild(new Text(mgr, div, "B")); + Rooted myTag{new Element{mgr, div, "myTag", {}, "myNameSpace"}}; + div->addChild(myTag); + myTag->addChild(new Text(mgr, myTag, "C")); // Now this is what we expect to see: std::string expected{ "\n" "\n" "\t\n" - "\t\t\n" - "\t\t\tmy title\n" - "\t\t\n" + "\t\tmy title\n" "\t\n" "\t\n" - "\t\t
\n" - "\t\t\t

\n" - "\t\t\t\tmy text\n" - "\t\t\t

\n" - "\t\t\t

\n" - "\t\t\t\tmy text\n" - "\t\t\t

\n" - "\t\t
\n" + "\t\t

A

BC
\n" "\t\n" "\n"}; -- cgit v1.2.3