如何将多个对象序列化为现有的XmlDocument,而不在每个组件上都有命名空间?

如何在.Net / C#中将多个对象序列化为现有的XmlDocument?

我有一个XmlDocument,它已经包含数据。 我有多个对象。 现在我想逐个序列化它们并将它们添加到XmlDocument(AppendChild)。

这应该是这样的:

  ...  anydata  ...  anydata   

当我使用XmlSerializer时,我得到每个组件的这个定义:

  anydata  

所以这就是我得到的,当我序列化为一个字符串然后从字符串创建一个XmlNode时,我追加到我的文档:

   ...  anydata  ...  anydata   

我可以通过这样做删除命名空间:

 XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); StringWriter xout = new StringWriter(); x.Serialize(xout, data, ns); 

但后来我在对象数组中的任何对象上获取名称空间。 这个对象:

 public class component_1 { object[] arr; } 

将被序列化为:

   one two   

是否可以将所有需要的命名空间等添加到我的文档中,然后将对象序列化为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(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(string xmlString) { return (T)(new XmlSerializer(typeof(T))).Deserialize(new StringReader(xmlString)); } 

也许可以在文档(rootNode)中添加名称空间,并在使用函数XmlDocument.ReadNode从字符串创建新的XmlNode时,通过XmlDocument中的名称空间来解析字符串中的名称空间。 但我不知道如何。

更新2:
感谢Alex Filipovici,序列化输出正是我想要的。

 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(project.ChildNodes[0].OuterXml); Console.WriteLine(c.objectArray[0].GetType()); // -> System.Xml.XmlNode[] ! } 

输出:

   Component_1.Value  one two    Component_1.Value  one two    

但是现在使用上面的“T desirialize(string xmlString)”函数进行反序列化失败了。 对象数组包含XmlNodes。

是否可以告诉XmlSerializer使用项目节点中的命名空间,还是必须再次插入它们?

这将序列化对象并将它们附加到XmlDocument。 解除序列化代码将解析命名空间。 @Alex:感谢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(root.ChildNodes[0]); // deserialize XmlNode to object } T deserialize(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 _typeCache; 

将映射到相应Type值的预期XML类型添加到其中:

 _typeCache = new Dictionary(); _typeCache.Add("string", typeof(System.String)); _typeCache.Add("int", typeof(System.Int32)); _typeCache.Add("dateTime", typeof(System.DateTime)); 

并通过将其转换为相应的预期类型来替换数组中的每个XmlNode

 Component_1 c = Deserialize(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