closes #1
This commit is contained in:
@@ -35,10 +35,6 @@ class B: public xml::Serialize {
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& fn(std::ostream& os) {
|
||||
os<<std::endl<<"HALLO"<<std::endl;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
{ // Serialization as a member
|
||||
std::stringstream ss("<b>\n"
|
||||
@@ -56,7 +52,6 @@ int main(int, char**) {
|
||||
std::cout<<"Text B: "<<b.txt<<std::endl;
|
||||
std::cout<<"Text A: "<<b.a.txt<<std::endl;
|
||||
b.saveXml(std::cout)<<std::endl;
|
||||
std::cout<<fn;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ AM_LDFLAGS = -L${top_builddir}/src -lxml-cxx
|
||||
|
||||
noinst_PROGRAMS = address node_macros serialization \
|
||||
contain_serialization inherit_serialization \
|
||||
list_serialization
|
||||
list_serialization optional_serialization
|
||||
|
||||
address_SOURCES = address.cxx
|
||||
node_macros_SOURCES = node_macros.cxx
|
||||
@@ -16,5 +16,6 @@ serialization_SOURCES = serialization.cxx
|
||||
contain_serialization_SOURCES = contain_serialization.cxx
|
||||
inherit_serialization_SOURCES = inherit_serialization.cxx
|
||||
list_serialization_SOURCES = list_serialization.cxx
|
||||
optional_serialization_SOURCES = optional_serialization.cxx
|
||||
|
||||
MAINTAINERCLEANFILES = makefile.in
|
||||
|
46
doc/examples/optional_serialization.cxx
Normal file
46
doc/examples/optional_serialization.cxx
Normal file
@@ -0,0 +1,46 @@
|
||||
/*! @file
|
||||
|
||||
@id $Id$
|
||||
*/
|
||||
// 1 2 3 4 5 6 7 8
|
||||
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
|
||||
#include <xml-cxx/xml.hxx>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
class A: public xml::Serialize {
|
||||
public:
|
||||
xml::Optional<int> a;
|
||||
xml::Optional<std::string> txt;
|
||||
protected:
|
||||
void initXmlMembers() {
|
||||
className("A");
|
||||
persist(a, "a");
|
||||
persist(txt, "txt");
|
||||
}
|
||||
};
|
||||
|
||||
class B: public xml::Serialize {
|
||||
public:
|
||||
xml::Optional<A> a;
|
||||
xml::Optional<int> i;
|
||||
xml::Optional<std::string> txt;
|
||||
protected:
|
||||
void initXmlMembers() {
|
||||
className("B");
|
||||
persist(a, "a");
|
||||
persist(i, "i");
|
||||
persist(txt, "txt");
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
{ // Serialization as a member
|
||||
std::stringstream ss("<B/>");
|
||||
B b;
|
||||
b.loadXml(ss);
|
||||
b.saveXml(std::cout)<<std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
@@ -165,9 +165,9 @@ class MethodTrace {
|
||||
- most standard containers, but in their xml-form,
|
||||
e.g. xml::List instead of @c std::list
|
||||
(xml::List inherits @c std::list)
|
||||
- Optional values through xml::Optional
|
||||
- @c std::bitset, @c std::priority_queue, @c std::queue and
|
||||
@c std::stack are not implemented
|
||||
- Optional values are not yet implemented
|
||||
- Polymorfic serialisation is not yet implemented
|
||||
|
||||
@page rationale Rationale - Limitations of other libraries
|
||||
@@ -787,6 +787,9 @@ namespace xml {
|
||||
virtual Node& text(const std::string& txt) throw(tag_expected,
|
||||
type_mismatch);
|
||||
virtual Node& append(const Node& o) throw(cannot_have_children);
|
||||
virtual Node& remove(Node& n) throw(access_error);
|
||||
virtual Node& remove(const std::string& n) throw(access_error);
|
||||
virtual Node& remove(size_type n) throw(out_of_range);
|
||||
virtual Node& set(const Attributes& o) throw();
|
||||
Node& clear() throw ();
|
||||
std::string name() const throw();
|
||||
@@ -954,6 +957,7 @@ namespace xml {
|
||||
private:
|
||||
friend class stream_error;
|
||||
friend class Serialize;
|
||||
template<class T> friend class Optional;
|
||||
template<class T> friend class Container;
|
||||
template<class T> friend class AssociativeContainer;
|
||||
template<class T> friend class AssociativeMap;
|
||||
@@ -1098,8 +1102,8 @@ namespace xml {
|
||||
Serialize(const Serialize& other) throw();
|
||||
virtual ~Serialize();
|
||||
Serialize& operator=(const Serialize& other) throw();
|
||||
Serialize& className(const std::string& name) throw();
|
||||
virtual Serialize& persist(Serialize& member,
|
||||
virtual Serialize& className(const std::string& name) throw();
|
||||
Serialize& persist(Serialize& member,
|
||||
const std::string& name) throw();
|
||||
Serialize& persist(bool& member,
|
||||
const std::string& name) throw();
|
||||
@@ -1136,6 +1140,7 @@ namespace xml {
|
||||
static void registerFromNode(FromNodeFunc fromNodeFunc);
|
||||
static void registerToNode(ToNodeFunc toNodeFunc);
|
||||
static void registerClear(ClearFunc clearFunc);
|
||||
virtual void clear() throw();
|
||||
protected:
|
||||
virtual void initXmlMembers();
|
||||
void checkInit(const Serialize* const ser=0) const {
|
||||
@@ -1145,11 +1150,15 @@ namespace xml {
|
||||
if (!_xmlFactory) const_cast<Serialize*>(this)->initXmlMembers();
|
||||
}
|
||||
}
|
||||
virtual void clear() throw();
|
||||
/*! @todo Why does @c protected: not work here?!? Children can't
|
||||
access the members if they are protected! */
|
||||
public:
|
||||
//! @cond INTERNAL
|
||||
template<typename TYPE> friend bool assignFromNode(Any member,
|
||||
const xml::Node& node);
|
||||
template<typename TYPE> friend bool assigntoNode(Any member,
|
||||
const xml::Node& node);
|
||||
virtual bool optional() const throw();
|
||||
void clear(Any member) throw();
|
||||
void reset() throw();
|
||||
void copy(const Serialize& o) throw();
|
||||
@@ -1162,8 +1171,8 @@ namespace xml {
|
||||
_xmlFactory = schema;
|
||||
return *this;
|
||||
}
|
||||
void fromNode(Any member, const xml::Node& node);
|
||||
void toNode(const Any member, xml::Node& node) const;
|
||||
virtual void fromNode(Any member, const xml::Node& node);
|
||||
virtual void toNode(const Any member, xml::Node& node) const;
|
||||
std::map<std::string, Any> _xmlNames;
|
||||
xml::Factory _xmlFactory;
|
||||
static std::set<FromNodeFunc> _fromNode;
|
||||
@@ -1174,54 +1183,71 @@ namespace xml {
|
||||
|
||||
template <class TYPE> class Optional: public Serialize {
|
||||
public:
|
||||
Optional() throw() {}
|
||||
Optional(const Optional& o) throw(): _member(new TYPE(*o._member)) {}
|
||||
Optional(const TYPE& mem) throw(): _member(new TYPE(mem)) {}
|
||||
Optional() throw(): _valid(false) {}
|
||||
Optional(const Optional& o) throw():
|
||||
_member(o._member), _valid(o.valid) {
|
||||
}
|
||||
Optional(const TYPE& mem) throw():
|
||||
_member(mem), _valid(true) {
|
||||
}
|
||||
virtual ~Optional() throw() {}
|
||||
Optional& operator=(const Optional& o) throw() {
|
||||
_member = new TYPE(*o._member);
|
||||
_member = o._member;
|
||||
_valid = o._valid;
|
||||
return *this;
|
||||
}
|
||||
Optional& operator=(const TYPE& mem) throw() {
|
||||
_member = new TYPE(mem);
|
||||
_member = mem;
|
||||
_valid = true;
|
||||
return *this;
|
||||
}
|
||||
operator bool() const throw() {
|
||||
return _member.get();
|
||||
return _valid;
|
||||
}
|
||||
const TYPE& operator*() const throw() {
|
||||
return *_member;
|
||||
return _member;
|
||||
}
|
||||
TYPE& operator*() throw() {
|
||||
return *_member;
|
||||
return _member;
|
||||
}
|
||||
const TYPE*const operator->() const throw() {
|
||||
return _member.get();
|
||||
return &_member;
|
||||
}
|
||||
TYPE*const operator->() throw() {
|
||||
return _member.get();
|
||||
return &_member;
|
||||
}
|
||||
virtual void clear() throw() {
|
||||
_valid = false;
|
||||
}
|
||||
virtual Optional& className(const std::string& name) throw() {
|
||||
if (!_xmlFactory) {
|
||||
Serialize::className(name);
|
||||
persist(_member, name);
|
||||
// make the child the root, and it's optional
|
||||
_xmlFactory = (*_xmlFactory)[0];
|
||||
_xmlFactory->limits(0, 1);
|
||||
}
|
||||
Optional& reset() throw() {
|
||||
_member.reset();
|
||||
return *this;
|
||||
}
|
||||
protected:
|
||||
virtual void clear() throw() {
|
||||
_member.reset();
|
||||
virtual bool optional() const throw() {
|
||||
return true;
|
||||
}
|
||||
virtual std::ostream& saveXml(std::ostream& os,
|
||||
const std::string& name = std::string())
|
||||
const throw() {
|
||||
if (!_member.get()) return os;
|
||||
checkInit();
|
||||
xml::Node node(*_xmlFactory);
|
||||
if (name.size()) node.name(name);
|
||||
toNode(*_member, node);
|
||||
os<<node;
|
||||
return os;
|
||||
virtual void fromNode(Any member, const xml::Node& node) {
|
||||
_valid = true;
|
||||
Serialize::fromNode(Any(&_member), node);
|
||||
}
|
||||
virtual void toNode(const Any member, xml::Node& node) const {
|
||||
if (!_valid) {
|
||||
node.parent().remove(node);
|
||||
return;
|
||||
}
|
||||
const Any mem(&const_cast<Optional*>(this)->_member);
|
||||
Serialize::toNode(mem, node);
|
||||
}
|
||||
private:
|
||||
std::auto_ptr<TYPE> _member;
|
||||
TYPE _member;
|
||||
bool _valid;
|
||||
};
|
||||
|
||||
//! @addtogroup serialization
|
||||
@@ -1400,17 +1426,13 @@ namespace xml {
|
||||
if (name.size()) factory->name(name);
|
||||
std::auto_ptr<xml::Node> node(factory.read(is));
|
||||
CONTAINER_TYPE::clear();
|
||||
LOG("READING: "<<*node);
|
||||
for (xml::Node::size_type i(0); i<node->children(); ++i) {
|
||||
typename CONTAINER_TYPE::key_type key;
|
||||
typename CONTAINER_TYPE::mapped_type data;
|
||||
LOG("READING Key: "<<(*node)[i]<<"Value:"<<(*node)[1+i]);
|
||||
Serialize::fromNode(&key, (*node)[i]); // reads into tmp
|
||||
Serialize::fromNode(&data, (*node)[++i]); // key&value
|
||||
insert(typename CONTAINER_TYPE::value_type(key, data));
|
||||
LOG("READ");
|
||||
}
|
||||
LOG("DONE");
|
||||
return is;
|
||||
}
|
||||
virtual std::ostream& saveXml(std::ostream& os,
|
||||
|
44
src/xml.cxx
44
src/xml.cxx
@@ -9,6 +9,7 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
unsigned int MethodTrace::_level(0);
|
||||
|
||||
@@ -310,6 +311,25 @@ namespace xml {
|
||||
_contents.push_back(o.clone(this).release());
|
||||
return *this;
|
||||
}
|
||||
//! Removes a given child node.
|
||||
Node& Node::remove(Node& n) throw(access_error) {
|
||||
Contents::iterator it(std::find(_contents.begin(), _contents.end(), &n));
|
||||
if (it==_contents.end()) throw access_error(*this, n.name());
|
||||
_contents.erase(it);
|
||||
return *this;
|
||||
}
|
||||
//! Removes the first child node of a given name.
|
||||
Node& Node::remove(const std::string& n) throw(access_error) {
|
||||
Node* t(find(n));
|
||||
if (!t) throw access_error(*this, n);
|
||||
return remove(*t);
|
||||
}
|
||||
//! Removes the child node of a given position.
|
||||
Node& Node::remove(size_type n) throw(out_of_range) {
|
||||
if (n>=children()) throw out_of_range(*this, n);
|
||||
_contents.erase(_contents.begin()+n);
|
||||
return *this;
|
||||
}
|
||||
//! Set a list of attributes.
|
||||
/*! Existing attributes with the same name are overwritten. */
|
||||
Node& Node::set(const Attributes& o) throw() {
|
||||
@@ -1020,6 +1040,12 @@ namespace xml {
|
||||
}
|
||||
Serialize& Serialize::persist(Serialize& ser,
|
||||
const std::string& name) throw() {
|
||||
if (ser.optional()) {
|
||||
_xmlNames[name] = &ser;
|
||||
ser.className(name);
|
||||
*_xmlFactory<<*ser._xmlFactory;
|
||||
std::stringstream ss;
|
||||
} else {
|
||||
ser.checkInit();
|
||||
_xmlNames[name] = &ser;
|
||||
xml::Node schema(*_xmlFactory);
|
||||
@@ -1029,6 +1055,8 @@ namespace xml {
|
||||
_xmlFactory = schema<<node;
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Serialize& Serialize::persist(bool& member,
|
||||
const std::string& name) throw() {
|
||||
return persistSimpleType(member, name);
|
||||
@@ -1123,13 +1151,13 @@ namespace xml {
|
||||
void Serialize::registerClear(Serialize::ClearFunc clearFunc) {
|
||||
_clear.insert(clearFunc);
|
||||
}
|
||||
void Serialize::initXmlMembers() {}
|
||||
void Serialize::clear() throw() {
|
||||
for (std::map<std::string, Any>::const_iterator
|
||||
it(_xmlNames.begin());
|
||||
it!=_xmlNames.end(); ++it)
|
||||
clear(it->second);
|
||||
}
|
||||
void Serialize::initXmlMembers() {}
|
||||
void Serialize::reset() throw() {
|
||||
_xmlFactory.reset();
|
||||
_xmlNames.clear();
|
||||
@@ -1156,6 +1184,10 @@ namespace xml {
|
||||
if ((**it)(member)) return; // found match
|
||||
throw type_not_registered(member.type().name());
|
||||
}
|
||||
bool Serialize::optional() const throw() {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<Serialize::FromNodeFunc> Serialize::_fromNode;
|
||||
std::set<Serialize::ToNodeFunc> Serialize::_toNode;
|
||||
std::set<Serialize::ClearFunc> Serialize::_clear;
|
||||
@@ -1201,6 +1233,10 @@ namespace xml {
|
||||
template<> bool assignFromNode<Serialize>(Any member,
|
||||
const xml::Node& node) {
|
||||
if (!any_cast<Serialize>(&member)) return false;
|
||||
if (any_cast<Serialize>(member)->optional()) {
|
||||
(*any_cast<Serialize>(member)).fromNode(member, node);
|
||||
return true;
|
||||
}
|
||||
//! @todo improve this (inefficient)
|
||||
std::stringstream ss; // simple but inefficient: rewrite and reread
|
||||
ss<<node;
|
||||
@@ -1214,6 +1250,10 @@ namespace xml {
|
||||
template<> bool assignToNode<Serialize>(const Any member,
|
||||
xml::Node& node) {
|
||||
if (!any_cast<Serialize>(&member)) return false;
|
||||
if (any_cast<Serialize>(member)->optional()) {
|
||||
any_cast<Serialize>(member)->toNode(member, node);
|
||||
return true;
|
||||
}
|
||||
std::stringstream ss;
|
||||
any_cast<Serialize>(member)->saveXml(ss, node.name());
|
||||
xml::Factory factory(node);
|
||||
@@ -1238,7 +1278,7 @@ namespace xml {
|
||||
}
|
||||
template<> bool clearMember<Serialize>(Any member) {
|
||||
if (!any_cast<Serialize>(&member)) return false;
|
||||
any_cast<std::string>(member)->clear();
|
||||
any_cast<Serialize>(member)->clear();
|
||||
return true;
|
||||
}
|
||||
// Init To and From Node ---------------------------------------------------
|
||||
|
@@ -7,12 +7,15 @@ AM_CXXFLAGS += -I ${top_srcdir}/src
|
||||
AM_LDFLAGS = -L${top_builddir}/src
|
||||
LDADD = -lcppunit -lxml-cxx
|
||||
|
||||
check_PROGRAMS = xml_test serialization_test container_serialization_test
|
||||
check_PROGRAMS = xml_test serialization_test \
|
||||
container_serialization_test optional_serialization_test
|
||||
|
||||
TESTS=${check_PROGRAMS}
|
||||
|
||||
xml_test_SOURCES = xml_test.cxx
|
||||
serialization_test_SOURCES = serialization_test.cxx
|
||||
container_serialization_test_SOURCES = container_serialization_test.cxx
|
||||
optional_serialization_test_SOURCES = optional_serialization_test.cxx
|
||||
|
||||
CLEANFILES =
|
||||
MAINTAINERCLEANFILES = makefile.in
|
||||
|
118
test/optional_serialization_test.cxx
Normal file
118
test/optional_serialization_test.cxx
Normal file
@@ -0,0 +1,118 @@
|
||||
/*! @file
|
||||
|
||||
@id $Id: serialization_test.cxx 31 2009-04-28 07:36:07Z $
|
||||
*/
|
||||
// 1 2 3 4 5 6 7 8
|
||||
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
|
||||
#include <xml-cxx/xml.hxx>
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/ui/text/TestRunner.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||
#include <cppunit/XmlOutputter.h>
|
||||
#include <fstream>
|
||||
|
||||
class A: public xml::Serialize {
|
||||
public:
|
||||
xml::Optional<int> _int;
|
||||
protected:
|
||||
void initXmlMembers() {
|
||||
className("A");
|
||||
persist(_int, "int");
|
||||
}
|
||||
};
|
||||
|
||||
class B: public xml::Serialize {
|
||||
public:
|
||||
xml::Optional<long> _long;
|
||||
xml::Optional<short> _short;
|
||||
protected:
|
||||
void initXmlMembers() {
|
||||
className("B");
|
||||
persist(_long, "long");
|
||||
persist(_short, "short");
|
||||
}
|
||||
};
|
||||
|
||||
class B1: public B {
|
||||
public:
|
||||
xml::Optional<A> _a1;
|
||||
xml::Optional<A> _a2;
|
||||
xml::Optional<A> _a3;
|
||||
protected:
|
||||
void initXmlMembers() {
|
||||
B::initXmlMembers();
|
||||
className("B1");
|
||||
persist(_a1, "a1");
|
||||
persist(_a2, "a2");
|
||||
persist(_a3, "a3");
|
||||
}
|
||||
};
|
||||
|
||||
class OptionalSerializationTest: public CppUnit::TestFixture {
|
||||
public:
|
||||
void checkOptional() {
|
||||
std::string with("<A>\n"
|
||||
"\t<int>13</int>\n"
|
||||
"</A>");
|
||||
std::string without("<A/>");
|
||||
A a;
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("<A>\n"
|
||||
"\t<int/>\n"
|
||||
"</A>"), a.schema());
|
||||
std::stringstream ss1a(with);
|
||||
CPPUNIT_ASSERT_NO_THROW(a.loadXml(ss1a));
|
||||
CPPUNIT_ASSERT_EQUAL(true, (bool)a._int);
|
||||
CPPUNIT_ASSERT_EQUAL(13, *a._int);
|
||||
std::stringstream ss1b;
|
||||
CPPUNIT_ASSERT_NO_THROW(a.saveXml(ss1b));
|
||||
CPPUNIT_ASSERT_EQUAL(with, ss1b.str());
|
||||
std::stringstream ss2a(without);
|
||||
CPPUNIT_ASSERT_NO_THROW(a.loadXml(ss2a));
|
||||
CPPUNIT_ASSERT_EQUAL(false, (bool)a._int);
|
||||
std::stringstream ss2b;
|
||||
CPPUNIT_ASSERT_NO_THROW(a.saveXml(ss2b));
|
||||
CPPUNIT_ASSERT_EQUAL(without, ss2b.str());
|
||||
}
|
||||
void checkMoreOptional() {
|
||||
std::string first("<B1>\n"
|
||||
"\t<short>13</short>\n"
|
||||
"\t<a2/>\n"
|
||||
"\t<a3>\n"
|
||||
"\t\t<int>42</int>\n"
|
||||
"\t</a3>\n"
|
||||
"</B1>");
|
||||
B1 b;
|
||||
std::stringstream ifirst(first);
|
||||
CPPUNIT_ASSERT_NO_THROW(b.loadXml(ifirst));
|
||||
CPPUNIT_ASSERT_EQUAL(false, (bool)b._long);
|
||||
CPPUNIT_ASSERT_EQUAL(true, (bool)b._short);
|
||||
CPPUNIT_ASSERT_EQUAL((short)13, *b._short);
|
||||
CPPUNIT_ASSERT_EQUAL(false, (bool)b._a1);
|
||||
CPPUNIT_ASSERT_EQUAL(true, (bool)b._a2);
|
||||
CPPUNIT_ASSERT_EQUAL(false, (bool)b._a2->_int);
|
||||
CPPUNIT_ASSERT_EQUAL(true, (bool)b._a3);
|
||||
CPPUNIT_ASSERT_EQUAL(true, (bool)b._a3->_int);
|
||||
CPPUNIT_ASSERT_EQUAL(42, *b._a3->_int);
|
||||
std::stringstream ofirst;
|
||||
CPPUNIT_ASSERT_NO_THROW(b.saveXml(ofirst));
|
||||
CPPUNIT_ASSERT_EQUAL(first, ofirst.str());
|
||||
}
|
||||
CPPUNIT_TEST_SUITE(OptionalSerializationTest);
|
||||
CPPUNIT_TEST(checkOptional);
|
||||
CPPUNIT_TEST(checkMoreOptional);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(OptionalSerializationTest);
|
||||
|
||||
int main(int argc, char** argv) try {
|
||||
std::ofstream ofs((*argv+std::string(".xml")).c_str());
|
||||
CppUnit::TextUi::TestRunner runner;
|
||||
//runner.setOutputter(new CppUnit::XmlOutputter(&runner.result(), ofs));
|
||||
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
|
||||
return runner.run() ? 0 : 1;
|
||||
} catch (std::exception& e) {
|
||||
std::cerr<<"***Exception: "<<e.what()<<std::endl;
|
||||
return 1;
|
||||
}
|
Reference in New Issue
Block a user