我需要使用动态标记名称在C#上将对象数组序列化为XML。我创建了两个需要序列化为xml的类,但我无法弄清楚如何使用动态名称创建标记。
例如,我有以下类:
[System.Xml.Serialization.XmlRootAttribute(IsNullable = false)]
public class GeneralInformation
{
private Info[] addInfoList;
/// <remarks/>
[System.Xml.Serialization.XmlArray("InfoList")]
public Info[] AddInfoList
{
get
{
return this.addInfoList;
}
set
{
this.addInfoList = value;
}
}
}
public class Info
{
private string infoMessage;
/// <remarks/>
[System.Xml.Serialization.XmlElement("InfoName")]
public string InfoMessage
{
get
{
return this.infoMessage;
}
set
{
this.infoMessage = value;
}
}
}
如果我添加一些简单数据并将其序列化,我会得到:
<?xml version="1.0" encoding="utf-16"?>
<GeneralInformation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<InfoList>
<Info>
<InfoName>Test1</InfoName>
</Info>
<Info>
<InfoName>Test2</InfoName>
</Info>
<Info>
<InfoName>Test3</InfoName>
</Info>
</InfoList>
</GeneralInformation>
但是我需要用数组的索引+1来枚举标签“Info”。这可能吗?结果看起来像这样。
<?xml version="1.0" encoding="utf-16"?>
<GeneralInformation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<InfoList>
<Info001>
<InfoName>Test1</InfoName>
</Info001>
<Info002>
<InfoName>Test2</InfoName>
</Info002>
<Info003>
<InfoName>Test3</InfoName>
</Info003>
</InfoList>
</GeneralInformation>
注意我只需要将GeneralInformation
序列化为XML,而不是反序列化。
答案 0 :(得分:2)
使用XDocument:
Category
答案 1 :(得分:0)
<InfoList>
元素的值最自然地表示为Dictionary<string, Info>
,但不幸的是XmlSerializer
does not support dictionaries。
相反,由于您要收集数组中的Info
个对象,因此您可以使用从this answer到 Deserialize XML with XmlSerializer where XmlElement names differ but have same content 的方法并序列化您的{{ 1}}数组通过[XmlAnyElement("InfoList")] public XElement
代理属性,其中Info []
实例通过构造嵌套的Info
被序列化为命名元素。
首先,按如下方式定义XmlSerializer
:
GeneralInformation
然后,从Deserialize XML with XmlSerializer where XmlElement names differ but have same content逐字抓取[System.Xml.Serialization.XmlRootAttribute(IsNullable = false)]
public class GeneralInformation
{
private Info[] addInfoList;
/// <remarks/>
[XmlIgnore]
public Info[] AddInfoList
{
get
{
return this.addInfoList;
}
set
{
this.addInfoList = value;
}
}
const string InfoPrefix = "Info";
const string InfoListPrefix = "InfoList";
[XmlAnyElement("InfoList")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public XElement AddInfoListXml
{
get
{
if (addInfoList == null)
return null;
return new XElement(InfoListPrefix,
addInfoList
.Select((info, i) => new KeyValuePair<string, Info>(InfoPrefix + (i + 1).ToString("D3", NumberFormatInfo.InvariantInfo), info))
.SerializeToXElements((XNamespace)""));
}
set
{
if (value == null)
{
addInfoList = null;
}
else
{
addInfoList = value
.Elements()
.Where(e => e.Name.LocalName.StartsWith(InfoPrefix))
.DeserializeFromXElements<Info>()
.Select(p => p.Value)
.ToArray();
}
}
}
}
和XmlKeyValueListHelper
:
XmlSerializerFactory
注意:
通过在根级别应用于public static class XmlKeyValueListHelper
{
const string RootLocalName = "Root";
public static XElement [] SerializeToXElements<T>(this IEnumerable<KeyValuePair<string, T>> dictionary, XNamespace ns)
{
if (dictionary == null)
return null;
ns = ns ?? "";
var serializer = XmlSerializerFactory.Create(typeof(T), RootLocalName, ns.NamespaceName);
var array = dictionary
.Select(p => new { p.Key, Value = p.Value.SerializeToXElement(serializer, true) })
// Fix name and remove redundant xmlns= attributes. XmlWriter will add them back if needed.
.Select(p => new XElement(ns + p.Key, p.Value.Attributes().Where(a => !a.IsNamespaceDeclaration), p.Value.Elements()))
.ToArray();
return array;
}
public static IEnumerable<KeyValuePair<string, T>> DeserializeFromXElements<T>(this IEnumerable<XElement> elements)
{
if (elements == null)
yield break;
XmlSerializer serializer = null;
XNamespace ns = null;
foreach (var element in elements)
{
if (serializer == null || element.Name.Namespace != ns)
{
ns = element.Name.Namespace;
serializer = XmlSerializerFactory.Create(typeof(T), RootLocalName, ns.NamespaceName);
}
var elementToDeserialize = new XElement(ns + RootLocalName, element.Attributes(), element.Elements());
yield return new KeyValuePair<string, T>(element.Name.LocalName, elementToDeserialize.Deserialize<T>(serializer));
}
}
public static XmlSerializerNamespaces NoStandardXmlNamespaces()
{
var ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
return ns;
}
public static XElement SerializeToXElement<T>(this T obj)
{
return obj.SerializeToXElement(null, NoStandardXmlNamespaces());
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializerNamespaces ns)
{
return obj.SerializeToXElement(null, ns);
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
return obj.SerializeToXElement(serializer, (omitStandardNamespaces ? NoStandardXmlNamespaces() : null));
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj, ns);
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
return (T)result;
}
}
}
public static class XmlSerializerFactory
{
// To avoid a memory leak the serializer must be cached.
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
// This factory taken from
// https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;
static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}
属性的[XmlAnyElement("InfoList")]
构造函数中指定"InfoList"
名称,只有名为public XElement AddInfoListXml
的元素及其内容将为通过代理人反序列化。
原始<InfoList>
属性必须标有AddInfoList
,因此不会与代理人一起序列化。
使用实现IXmlSerializable
的自定义集合替换[XmlIgnore]
数组将是解决此问题的另一种方法,但正确实现AddInfoList
可能非常棘手。请参阅 How do you deserialize XML with dynamic element names? 以获取比此处所示更简单的示例,因为该问题中的元素仅包含文本内容。
您的示例XML现在可以反序列化并重新序列化,如示例.Net小提琴here所示,生成以下XML:
IXmlSerializable
答案 2 :(得分:0)
您可以使用自定义xml编写器。
public class CustomWriter : XmlTextWriter
{
private int counter = 1;
public CustomWriter(TextWriter writer) : base(writer) { }
public CustomWriter(Stream stream, Encoding encoding) : base(stream, encoding) { }
public CustomWriter(string filename, Encoding encoding) : base(filename, encoding) { }
public override void WriteStartElement(string prefix, string localName, string ns)
{
if (localName == "Info")
{
base.WriteStartElement(prefix, localName + counter.ToString("0##"), ns);
counter++;
}
else
{
base.WriteStartElement(prefix, localName, ns);
}
}
}
使用:
var xs = new XmlSerializer(typeof(GeneralInformation));
using (var writer = new CustomWriter(Console.Out))
{
writer.Formatting = Formatting.Indented;
xs.Serialize(writer, data);
}
命名空间:
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;