有没有办法在序列化 class 的一部分时保留 XML 属性?
Is there a way to retain XML Attributes when serializing part of a class?
我正在尝试序列化 class 的一部分。我已将 XML 属性添加到 class 成员,以便生成的 XML 标记被正确命名以匹配规范,而不管我的属性被命名为什么。这在序列化 main class 时工作正常。但是,如果我只想序列化 class 的一部分,我会丢失 XML 属性并且名称会恢复为默认值。有没有办法在仅序列化 class 的一部分时保留 XML 属性?
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
当我序列化整个 class 时,我得到了这个(这正是我所期望的):
<someConfiguration>
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
</someConfiguration>
如果我尝试只序列化 class 的 'Bugs' 部分,我会得到这个(注意更改标签名称的 XML 属性全部被忽略):
<ArrayOfString>
<string>Bug1</string>
<string>Bug2</string>
<string>Bug3</string>
</ArrayOfString>
我需要得到这个:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
如何让部分 class 与上述标签序列化?
或者更好的是,在序列化一个简单的 List<object>
时,有没有办法指定标签名称。这样您就可以指定用于列表的标签而不是使用 <ArrayOfobject>
并指定用于数组项的标签而不是 <object>
?
is there a way to specify tag names when serializing a simple List.
一般来说,根据具体情况,它可能会起作用。请参阅 MSDN 的 How to: Specify an Alternate Element Name for an XML Stream。那里的示例涉及覆盖特定字段的序列化,但也可以使用相同的技术来覆盖整个类型名称。
但这对我来说似乎很麻烦。相反,为什么不直接处理序列化:
private static string SerializeByLinqAndToString<T>(
List<T> data, string rootName, string elementName)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(s => new XElement(elementName, s))));
return SaveXmlToString(document);
}
private static string SaveXmlToString(XDocument document)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(sb,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
document.Save(xmlWriter);
}
return sb.ToString();
}
这样调用:
SomeConfiguration config = ...; // initialize as desired
string result = SerializeByLinq(config.Bugs, "bug", "bugs");
以上仅适用于字符串列表或类型列表,其中元素内容可以只是对类型实例调用 ToString()
的结果。
在处理复杂类型时,使用 .NET 中成熟的序列化功能可能是值得的,但如果您拥有的只是一个简单的字符串列表,那么 LINQ-to-XML 功能是非常方便。
如果您有更复杂的类型,您可以将每个列表元素转换为 DOM 的 XElement
并序列化:
private static string SerializeByLinq<T>(
List<T> data, string rootName, string elementName = null)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(t =>
ElementFromText(SerializeObject(t), elementName)
)));
return SaveXmlToString(document);
}
private static XElement ElementFromText(string xml, string name = null)
{
StringReader reader = new StringReader(xml);
XElement result = XElement.Load(reader);
if (!string.IsNullOrEmpty(name))
{
result.Name = name;
}
return result;
}
private static string SerializeObject<T>(T o)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(textWriter,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
xmlSerializer.Serialize(writer, o,
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty}));
}
return textWriter.ToString();
}
在第二个示例中,您可以省略子元素的元素名称,它将只使用已设置为使用的任何类型(例如类型名称,或 [XmlRoot]
设置的任何内容) .
找到了 'work-around' 的方法..而不是将 XMLArray 和 XMLArrayList 属性放在 List<> 之上:
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
在列表上放置一个 XmlElement 属性,该属性将指定要用于每个元素的标签,而不是用标签包装列表。您的 class 标签实际上会为您做到这一点。
[XmlRoot ("bugs")]
public class SomeConfiguration
{
[XmlElement("bug")]
public List<string> Bugs { get; set; }
}
当你序列化上面的内容时,你会得到:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
只需将它扔在那里,您就可以将 List<> 包装在自定义 class:
中
[XmlRoot("config")]
public class SomeConfiguration
{
[XmlElement("bugs")]
public BugList Bugs { get; set; }
[XmlElement("trees")]
public TreeList Trees { get; set; }
}
[XmlRoot("bugs")]
public class BugList
{
[XmlElement("bug")]
public List<string> Items = new List<string>();
}
[XmlRoot("trees")]
public class TreeList
{
[XmlElement("tree")]
public List<string> Items = new List<string>();
}
这将允许您序列化各个列表,它们将如您所愿地植根。
void Main()
{
var config = new SomeConfiguration
{
Bugs = new BugList { Items = { "Bug1", "Bug2" } },
Trees = new TreeList { Items = { "Tree1", "Tree2" } }
};
// Your config will work as normal.
Debug.WriteLine(ToXml(config)); // <config> <bugs>.. <trees>..</config>
// Your collections are now root-ed properly.
Debug.WriteLine(ToXml(config.Bugs)); // <bugs><bug>Bug1</bug><bug>Bug2</bug></bugs>
Debug.WriteLine(ToXml(config.Trees)); // <trees><tree>Tree1</tree><tree>Tree2</tree></trees>
}
public string ToXml<T>(T obj)
{
var ser = new XmlSerializer(typeof(T));
var emptyNs = new XmlSerializerNamespaces();
emptyNs.Add("","");
using (var stream = new MemoryStream())
{
ser.Serialize(stream, obj, emptyNs);
return Encoding.ASCII.GetString(stream.ToArray());
}
}
我正在尝试序列化 class 的一部分。我已将 XML 属性添加到 class 成员,以便生成的 XML 标记被正确命名以匹配规范,而不管我的属性被命名为什么。这在序列化 main class 时工作正常。但是,如果我只想序列化 class 的一部分,我会丢失 XML 属性并且名称会恢复为默认值。有没有办法在仅序列化 class 的一部分时保留 XML 属性?
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
当我序列化整个 class 时,我得到了这个(这正是我所期望的):
<someConfiguration>
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
</someConfiguration>
如果我尝试只序列化 class 的 'Bugs' 部分,我会得到这个(注意更改标签名称的 XML 属性全部被忽略):
<ArrayOfString>
<string>Bug1</string>
<string>Bug2</string>
<string>Bug3</string>
</ArrayOfString>
我需要得到这个:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
如何让部分 class 与上述标签序列化?
或者更好的是,在序列化一个简单的 List<object>
时,有没有办法指定标签名称。这样您就可以指定用于列表的标签而不是使用 <ArrayOfobject>
并指定用于数组项的标签而不是 <object>
?
is there a way to specify tag names when serializing a simple List.
一般来说,根据具体情况,它可能会起作用。请参阅 MSDN 的 How to: Specify an Alternate Element Name for an XML Stream。那里的示例涉及覆盖特定字段的序列化,但也可以使用相同的技术来覆盖整个类型名称。
但这对我来说似乎很麻烦。相反,为什么不直接处理序列化:
private static string SerializeByLinqAndToString<T>(
List<T> data, string rootName, string elementName)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(s => new XElement(elementName, s))));
return SaveXmlToString(document);
}
private static string SaveXmlToString(XDocument document)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(sb,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
document.Save(xmlWriter);
}
return sb.ToString();
}
这样调用:
SomeConfiguration config = ...; // initialize as desired
string result = SerializeByLinq(config.Bugs, "bug", "bugs");
以上仅适用于字符串列表或类型列表,其中元素内容可以只是对类型实例调用 ToString()
的结果。
在处理复杂类型时,使用 .NET 中成熟的序列化功能可能是值得的,但如果您拥有的只是一个简单的字符串列表,那么 LINQ-to-XML 功能是非常方便。
如果您有更复杂的类型,您可以将每个列表元素转换为 DOM 的 XElement
并序列化:
private static string SerializeByLinq<T>(
List<T> data, string rootName, string elementName = null)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(t =>
ElementFromText(SerializeObject(t), elementName)
)));
return SaveXmlToString(document);
}
private static XElement ElementFromText(string xml, string name = null)
{
StringReader reader = new StringReader(xml);
XElement result = XElement.Load(reader);
if (!string.IsNullOrEmpty(name))
{
result.Name = name;
}
return result;
}
private static string SerializeObject<T>(T o)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(textWriter,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
xmlSerializer.Serialize(writer, o,
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty}));
}
return textWriter.ToString();
}
在第二个示例中,您可以省略子元素的元素名称,它将只使用已设置为使用的任何类型(例如类型名称,或 [XmlRoot]
设置的任何内容) .
找到了 'work-around' 的方法..而不是将 XMLArray 和 XMLArrayList 属性放在 List<> 之上:
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
在列表上放置一个 XmlElement 属性,该属性将指定要用于每个元素的标签,而不是用标签包装列表。您的 class 标签实际上会为您做到这一点。
[XmlRoot ("bugs")]
public class SomeConfiguration
{
[XmlElement("bug")]
public List<string> Bugs { get; set; }
}
当你序列化上面的内容时,你会得到:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
只需将它扔在那里,您就可以将 List<> 包装在自定义 class:
中[XmlRoot("config")]
public class SomeConfiguration
{
[XmlElement("bugs")]
public BugList Bugs { get; set; }
[XmlElement("trees")]
public TreeList Trees { get; set; }
}
[XmlRoot("bugs")]
public class BugList
{
[XmlElement("bug")]
public List<string> Items = new List<string>();
}
[XmlRoot("trees")]
public class TreeList
{
[XmlElement("tree")]
public List<string> Items = new List<string>();
}
这将允许您序列化各个列表,它们将如您所愿地植根。
void Main()
{
var config = new SomeConfiguration
{
Bugs = new BugList { Items = { "Bug1", "Bug2" } },
Trees = new TreeList { Items = { "Tree1", "Tree2" } }
};
// Your config will work as normal.
Debug.WriteLine(ToXml(config)); // <config> <bugs>.. <trees>..</config>
// Your collections are now root-ed properly.
Debug.WriteLine(ToXml(config.Bugs)); // <bugs><bug>Bug1</bug><bug>Bug2</bug></bugs>
Debug.WriteLine(ToXml(config.Trees)); // <trees><tree>Tree1</tree><tree>Tree2</tree></trees>
}
public string ToXml<T>(T obj)
{
var ser = new XmlSerializer(typeof(T));
var emptyNs = new XmlSerializerNamespaces();
emptyNs.Add("","");
using (var stream = new MemoryStream())
{
ser.Serialize(stream, obj, emptyNs);
return Encoding.ASCII.GetString(stream.ToArray());
}
}