複数のオブジェクトを.Net/C#の既存のXmlDocumentにシリアル化するにはどうすればよいですか?
すでにデータが含まれているXmlDocumentがあります。複数のオブジェクトがあります。次に、それらを1つずつシリアル化し、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()は、2つのオブジェクトをシリアル化し、それらをドキュメントに追加します。最後の行は、最初のオブジェクトを逆シリアル化します。
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 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<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(stringxmlString)」関数を使用した逆シリアル化は失敗します。オブジェクト配列にはXmlNodesが含まれています。
XmlSerializerにプロジェクトノードの名前空間を使用するように指示することは可能ですか、それともそれらを再度挿入する必要がありますか?