如何将多个对象序列化到现有的XmlDocument中,而不在每个组件上都有命名空间
本文关键字:组件 命名空间 对象 序列化 XmlDocument | 更新日期: 2023-09-27 17:58:09
如何在.Net/C#中将多个对象序列化到现有的XmlDocument中?
我有一个XmlDocument,它已经包含数据。我有多个对象。现在我想逐一序列化它们,并将它们添加到XmlDocument(AppendChild)中。
应该是这样的:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<project>
<mySettings>...</mySettings>
<component_1> anydata </component_1>
...
<component_x> anydata </component_x>
</project>
当我使用XmlSerializer时,我会得到每个组件的定义:
<component_1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
anydata
</component_1>
因此,当我序列化到一个字符串中,然后从字符串中创建一个XmlNode,并将其附加到我的文档中时,我会得到这样的结果:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<project>
<mySettings>...</mySettings>
<component_1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> anydata </component_1>
...
<component_x xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> anydata </component_x>
</project>
我可以通过以下操作删除名称空间:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
StringWriter xout = new StringWriter();
x.Serialize(xout, data, ns);
但是,我得到了对象数组中任何对象的名称空间。此对象:
public class component_1
{
object[] arr;
}
将被序列化为:
<component_1>
<objectArray>
<anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d3p1:type="q1:string" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">one</anyType>
<anyType xmlns:q2="http://www.w3.org/2001/XMLSchema" d3p1:type="q2:string" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">two</anyType>
</objectArray>
</component_1>
是否可以将所有需要的名称空间等添加到我的文档中,然后将对象序列化到XmlNodes中并将它们添加到文档中,而不需要在每个组件上都有名称空间?
更新:函数test()将序列化两个对象,并将它们附加到文档中。最后一行将反序列化第一个对象。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
...
public class Component_1
{
public string Value = "Component_1.Value";
public object[] objectArray = new object[] { "one", "two" };
}
void test()
{
object[] components = new object[] { new Component_1(), new Component_1() };
XmlDocument doc = new XmlDocument();
XmlNode rootNode = doc.AppendChild(doc.CreateElement("project"));
foreach (var component in components)
rootNode.AppendChild(doc.ReadNode(XmlTextReader.Create(new StringReader(serialize(component, true)))));
Console.WriteLine(doc.OuterXml);
Console.WriteLine(deserialize<Component_1>(rootNode.ChildNodes[0].OuterXml).Value);
}
string serialize(object obj, bool namespaces)
{
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { OmitXmlDeclaration = true });
(new XmlSerializer(obj.GetType())).Serialize(writer, obj, namespaces ? null : new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName("", "") }));
return sb.ToString();
}
T deserialize<T>(string xmlString)
{
return (T)(new XmlSerializer(typeof(T))).Deserialize(new StringReader(xmlString));
}
也许可以将命名空间添加到文档(rootNode)中,并在使用函数XmlDocument.ReadNode从字符串创建新的XmlNode时,通过XmlDocument中的命名空间解析字符串中的命名空间。但我不知道怎么做。
更新2:
感谢Alex Filippovici,序列化输出正是我想要的。
void test2()
{
object[] components = new object[] { new Component_1(), new Component_1() };
var doc = new XmlDocument();
var project = doc.AppendChild(doc.CreateElement("project"));
doc.DocumentElement.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
doc.DocumentElement.SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
var nav = project.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
foreach (var component in components)
{
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(component.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, component
, emptyNamepsaces
);
writer.Close();
}
}
foreach (XmlNode node in doc.GetElementsByTagName("anyType"))
{
string attributeType = "";
foreach (XmlAttribute xmlAttribute in node.Attributes)
{
if (xmlAttribute.LocalName == "type")
{
attributeType = xmlAttribute.Value.Split(':')[1];
}
}
node.Attributes.RemoveAll();
node.CreateNavigator().CreateAttribute("", "type", "", attributeType);
}
doc.Save("output.xml");
Component_1 c = deserialize<Component_1>(project.ChildNodes[0].OuterXml);
Console.WriteLine(c.objectArray[0].GetType()); // -> System.Xml.XmlNode[] !
}
输出:
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Component_1>
<Value>Component_1.Value</Value>
<objectArray>
<anyType type="string">one</anyType>
<anyType type="string">two</anyType>
</objectArray>
</Component_1>
<Component_1>
<Value>Component_1.Value</Value>
<objectArray>
<anyType type="string">one</anyType>
<anyType type="string">two</anyType>
</objectArray>
</Component_1>
</project>
但现在使用上面的"T desirialize(string xmlString)"函数进行的反序列化失败了。对象数组包含XmlNodes。
是否可以告诉XmlSerializer使用项目节点中的命名空间,或者我必须再次插入它们?
这将序列化对象并将它们附加到XmlDocument中。而对代码进行反序列化将解析名称空间。@亚历克斯:谢谢你用XPathNavigator做的例子。
void test2()
{
XmlDocument doc = new XmlDocument();
XmlNode root = doc.AppendChild(doc.CreateElement("root"));
doc.DocumentElement.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
doc.DocumentElement.SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
serializeAppend(root, new object[] { 1, "two", 3.0 }); // serialize object and append it to XmlNode
var obj = deserialize<object[]>(root.ChildNodes[0]); // deserialize XmlNode to object
}
T deserialize<T>(XmlNode node)
{
XPathNavigator nav = node.CreateNavigator();
using (var reader = nav.ReadSubtree())
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(reader);
}
}
void serializeAppend(XmlNode parentNode, object obj)
{
XPathNavigator nav = parentNode.CreateNavigator();
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(obj.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, obj);
writer.Close();
}
}
下面的代码将满足OP中的要求,即拥有干净的XML。它将删除所有元素中的所有三元属性,但它将为anyType
元素添加一个type
属性,因此每个元素的原始类型仍然可以区分。
static void Main(string[] args)
{
object[] components = new object[] { new Component_1(), new Component_1() };
var doc = new XmlDocument();
doc.Load("source.xml");
var project = doc.GetElementsByTagName("project")[0];
var nav = project.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty
});
foreach (var component in components)
{
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(component.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, component
, emptyNamepsaces
);
writer.Close();
}
}
foreach (XmlNode node in doc.GetElementsByTagName("anyType"))
{
string attributeType = "";
foreach (XmlAttribute xmlAttribute in node.Attributes)
{
if (xmlAttribute.LocalName == "type")
{
attributeType=xmlAttribute.Value.Split(':')[1];
}
}
node.Attributes.RemoveAll();
node.CreateNavigator().CreateAttribute("","type","",attributeType);
}
doc.Save("output.xml");
}
如果你想反序列化XML,你必须创建一个字典:
static Dictionary<string, Type> _typeCache;
将映射到相应Type
值的预期XML类型添加到其中:
_typeCache = new Dictionary<string, Type>();
_typeCache.Add("string", typeof(System.String));
_typeCache.Add("int", typeof(System.Int32));
_typeCache.Add("dateTime", typeof(System.DateTime));
并通过将阵列中的每个XmlNode
相应地转换为其预期类型来替换它:
Component_1 c = Deserialize<Component_1>(project.ChildNodes[0].OuterXml);
for (int i = 0; i < c.objectArray.Length; i++)
{
var type = _typeCache[(((System.Xml.XmlNode[])(c.objectArray[i]))[0]).Value];
var item = Convert.ChangeType((((System.Xml.XmlNode[])(c.objectArray[i]))[1]).Value, type);
c.objectArray[i] = item;
}
Console.WriteLine(c.objectArray[0].GetType()); // -> System.String