How to serialize multiple objects into a existing XmlDocument in .Net/C#?
I have a XmlDocument, which already contains data. I have multiple objects. Now I want to serialize them one by one and add them to the XmlDocument (AppendChild).
This is how it should be:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<project>
<mySettings>...</mySettings>
<component_1> anydata </component_1>
...
<component_x> anydata </component_x>
</project>
When I use the XmlSerializer I get this definition for each component:
<component_1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
anydata
</component_1>
So this is what I get, when I serialize into a string and then create a XmlNode from string, which i append to my document:
<?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>
I can remove the namespace by doing this:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
StringWriter xout = new StringWriter();
x.Serialize(xout, data, ns);
But then I get the namespaces on any object inside an object array. This object:
public class component_1
{
object[] arr;
}
will be serialized to:
<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>
Is it possible to add all needed namespaces, etc. to my document and then serialize objects into XmlNodes and add them to my document, without having the namespaces on each component?
UPDATE:
The function test() will serialize two objects and append them to a document.
Last line will deserialize the first object.
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));
}
Maybe it is possible to add namespaces to the document (rootNode) and when creating a new XmlNode from string with the function XmlDocument.ReadNode to resolve the namespaces in string by the namespaces from XmlDocument. But I don′t know how.
UPDATE 2:
Thanks Alex Filipovici, the serialization output is exact what I wanted.
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[] !
}
output:
<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>
But now the deserialization with the "T desirialize(string xmlString)" function from above fails. The object array contains XmlNodes.
Is it possible to tell XmlSerializer to use the namespaces from the project node, or do I have to insert them again?
See Question&Answers more detail:
os