【问题标题】:Using xml to load objects. Which is the best approach?使用 xml 加载对象。哪个是最好的方法?
【发布时间】:2009-11-05 01:30:47
【问题描述】:
TinyXML
我有一个 XML 文件,其中保存了一堆加载到对象中的数据。现在,我有一个巨大的方法来解析 XML 文件并根据 XML 文件的内容创建适当的对象。这个函数非常大,并且导入了大量的类定义。
每个类类型都从 XML 进行自己的加载会更好吗?这样,XML 代码分散在我的文件中,而不是在一个位置。问题是我需要将 XML 文件中该函数应该从中读取的确切节点传递给它。这可行吗?我正在使用 tinyxml,所以如果想象每个类都可以传递 XML 流(实际上是一个包含 XML 数据的数组),然后我还会传递该对象的根元素 \images\fractal\traversal\ 所以它知道它是什么应该在读书。
然后,保存将以相同的方式进行。
哪种方法最好且使用更广泛?
【问题讨论】:
标签:
c++
xml
design-patterns
xml-serialization
【解决方案1】:
我对 TinyXML 一无所知,但几年来我一直在使用 libxml2 的这种类设计,它对我来说运行良好。
【解决方案2】:
序列化函数应该是它们序列化的类的朋友。如果您想序列化和反序列化为 XML,您应该编写执行此功能的友元函数。您甚至可以编写自定义的ostream & operator <<() 函数来执行此操作,但是如果您想聚合对象,这会成为问题。更好的策略是定义一种机制,将单个对象转换为 DOM 文档中的节点。
【解决方案3】:
我可以想到一种方法,基于工厂根据标签提供对象。
这里的难点实际上并不是如何解耦每个对象内容的反序列化,而是如何解耦标签和对象的关联。
例如,假设您有以下 XML
<my_xml>
<bird> ... </bird>
</my_xml>
你怎么知道你应该用<bird>标签的内容构建一个Bird对象?
有两种方法:
- 1 对 1 映射,例如:
<my_xml> 表示单个对象,因此知道如何反序列化自身。
- 集合:
<my_xml> 只不过是一个松散的对象集合
第一个很明显,你知道会发生什么并且可以使用常规构造函数。
C++ 中的问题是你有静态类型,这使得第二种情况更加困难,因为你需要在那里进行虚拟构造。
虽然可以使用原型来实现虚拟构造。
// Base class
class Serializable:
{
public:
virtual std::auto_ptr<XmlNode*> serialize() const = 0;
virtual std::auto_ptr<Serializable> deserialize(const XmlNode&) const = 0;
};
// Collection of prototypes
class Deserializer:
{
public:
static void Register(Tag tag, const Serializable* item)
{
GetMap()[tag] = item;
}
std::auto_ptr<Serializable> Create(const XmlNode& node)
{
return GetConstMap()[node.tag()]->deserialize(node);
// I wish I could write that ;)
}
private:
typedef std::map<Tag, const Serializable*> prototypes_t;
prototypes_t& GetMap()
{
static prototypes_t _Map;
return _Map;
}
prototypes_t const& GetConstMap() { return GetMap(); }
};
// Example
class Bird: public Serializable
{
virtual std::auto_ptr<Bird> deserialize(const XmlNode& node);
};
// In some cpp (bird.cpp is indicated)
const Bird myBirdPrototype;
Deserializer::Register('bird', myBirdPrototype);
反序列化在 C++ 中总是有点混乱,动态类型确实有帮助:)
注意:它也适用于流式传输,但要安全地安装起来有点复杂。流式传输的问题是您应该确保不要读取过去的数据并读取所有数据,以便流处于下一个对象的“良好”状态:)