这是我试图解决的问题的一个虚构的例子。如果我在C#中工作,并且像这样使用XML:
<?xml version="1.0" encoding="utf-8"?>
<Cars>
<Car>
<StockNumber>1020</StockNumber>
<Make>Nissan</Make>
<Model>Sentra</Model>
</Car>
<Car>
<StockNumber>1010</StockNumber>
<Make>Toyota</Make>
<Model>Corolla</Model>
</Car>
<SalesPerson>
<Company>Acme Sales</Company>
<Position>
<Salary>
<Amount>1000</Amount>
<Unit>Dollars</Unit>
... and on... and on....
</SalesPerson>
</Cars>
SalesPerson中的XML可能非常长,大小为兆字节。我想反序列化标记,但不反序列化SalesPerson XML元素,而是将其保留为原始格式“以便以后再使用”。
基本上我希望能够将它用作XML的Objects表示。
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
public Stream SalesPerson { get; set; }
}
public class Car
{
[System.Xml.Serialization.XmlElementAttribute("StockNumber")]
public string StockNumber{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Make")]
public string Make{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Model")]
public string Model{ get; set; }
}
其中Cars对象上的SalesPerson属性将包含一个带有原始xml的流,该原始xml位于&lt; SalesPerson&gt;内。通过XmlSerializer运行后的xml元素。
可以这样做吗?我可以选择仅对xml文档的“部分”进行反序列化吗?
谢谢! -Mike
P.S。示例xml从How to Deserialize XML document
被盗答案 0 :(得分:36)
这可能是一个有点旧的线程,但无论如何我会发布。我有同样的问题(需要从一个超过1MB的文件反序列化10kb的数据)。在主对象(具有需要反序列化的InnerObject)中,我实现了一个IXmlSerializable接口,然后更改了ReadXml方法。
我们有xmlTextReader作为输入,第一行是读取XML标记:
reader.ReadToDescendant("InnerObjectTag"); //tag which matches the InnerObject
然后为我们想要反序列化和反序列化的对象类型创建XMLSerializer
XmlSerializer serializer = new XmlSerializer(typeof(InnerObject));
this.innerObject = serializer.Deserialize(reader.ReadSubtree()); //this gives serializer the part of XML that is for the innerObject data
reader.close(); //now skip the rest
这为我节省了大量反序列化的时间,并允许我只读取XML的一部分(只是描述文件的一些细节,这可能有助于用户决定文件是否是他想要加载的内容)。
答案 1 :(得分:7)
来自user271807的已接受answer是一个很好的解决方案,但我发现,我还需要设置片段的xml根,以避免异常,内部异常如下所示:
...xmlns=''> was not expected
当我尝试仅反序列化此xml文档的内部Authentication元素时,会抛出此异常:
<?xml version=""1.0"" encoding=""UTF-8""?>
<Api>
<Authentication>
<sessionid>xxx</sessionid>
<errormessage>xxx</errormessage>
</Authentication>
</ApI>
所以我最终创建了这个扩展方法作为可重用的解决方案 - 警告包含内存泄漏,见下文:
public static T DeserializeXml<T>(this string @this, string innerStartTag = null)
{
using (var stringReader = new StringReader(@this))
using (var xmlReader = XmlReader.Create(stringReader)) {
if (innerStartTag != null) {
xmlReader.ReadToDescendant(innerStartTag);
var xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute(innerStartTag));
return (T)xmlSerializer.Deserialize(xmlReader.ReadSubtree());
}
return (T)new XmlSerializer(typeof(T)).Deserialize(xmlReader);
}
}
2017年3月20日更新:正如下面的评论所指出的,使用XmlSerializer的一个构造函数时存在内存泄漏问题,因此我最终使用了一个缓存解决方案,如下所示: < / p>
/// <summary>
/// Deserialize XML string, optionally only an inner fragment of the XML, as specified by the innerStartTag parameter.
/// </summary>
public static T DeserializeXml<T>(this string @this, string innerStartTag = null) {
using (var stringReader = new StringReader(@this)) {
using (var xmlReader = XmlReader.Create(stringReader)) {
if (innerStartTag != null) {
xmlReader.ReadToDescendant(innerStartTag);
var xmlSerializer = CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute(innerStartTag));
return (T) xmlSerializer.Deserialize(xmlReader.ReadSubtree());
}
return (T) CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute("AutochartistAPI")).Deserialize(xmlReader);
}
}
}
/// <summary>
/// A caching factory to avoid memory leaks in the XmlSerializer class.
/// See http://dotnetcodebox.blogspot.dk/2013/01/xmlserializer-class-may-result-in.html
/// </summary>
public static class CachingXmlSerializerFactory {
private static readonly ConcurrentDictionary<string, XmlSerializer> Cache = new ConcurrentDictionary<string, XmlSerializer>();
public static XmlSerializer Create(Type type, XmlRootAttribute root) {
if (type == null) {
throw new ArgumentNullException(nameof(type));
}
if (root == null) {
throw new ArgumentNullException(nameof(root));
}
var key = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName);
return Cache.GetOrAdd(key, _ => new XmlSerializer(type, root));
}
public static XmlSerializer Create<T>(XmlRootAttribute root) {
return Create(typeof (T), root);
}
public static XmlSerializer Create<T>() {
return Create(typeof (T));
}
public static XmlSerializer Create<T>(string defaultNamespace) {
return Create(typeof (T), defaultNamespace);
}
public static XmlSerializer Create(Type type) {
return new XmlSerializer(type);
}
public static XmlSerializer Create(Type type, string defaultNamespace) {
return new XmlSerializer(type, defaultNamespace);
}
}
答案 2 :(得分:3)
您可以通过在类中实现ISerializable接口来控制序列化的完成方式。请注意,这也意味着一个带有方法签名的构造函数(SerializationInfo info,StreamingContext context),并确保你可以用它来做你想要的。
然而,仔细研究一下你是否真的需要使用流式传输,因为如果你不必使用流式传输机制,那么使用Linq to XML实现同样的功能将更容易,并且更易于维护从长远来看(IMO)
答案 3 :(得分:2)
我认为之前的评论者在他的评论中是正确的,XML可能不是这里支持商店的最佳选择。
如果您遇到规模问题并且没有利用XML所带来的其他一些细节,比如转换,那么最好使用数据库来处理数据。您正在进行的操作似乎更适合该模型。
我知道这并没有真正回答你的问题,但我想我会强调你可能会使用的替代解决方案。一个好的数据库和一个适当的OR映射器,如.netTiers,NHibernate,或者最近的LINQ to SQL / Entity Framework,可能会让你备份并运行,只需对代码库的其余部分进行最小的更改。
答案 4 :(得分:1)
通常,XML反序列化是开箱即用的全有或全无的命题,因此您可能需要自定义。如果不进行完全反序列化,则存在在SalesPerson元素中xml格式错误的风险,因此文档无效。
如果您愿意接受这种风险,您可能希望进行一些基本的文本解析,使用纯文本处理工具将SalesPerson元素分解为不同的文档,然后处理XML。
这是为什么XML并不总是正确答案的一个很好的例子。
答案 5 :(得分:1)
请尝试将SalesPerson属性定义为XmlElement
类型。这适用于使用XML序列化的ASMX Web服务的输出。我认为它也适用于输入。我希望整个<SalesPerson>
元素能够在XmlElement
中结束。
答案 6 :(得分:0)
您可以通过在Cars类上实现 IXmlSerializable 接口来控制Cars类的哪些部分反序列化,然后在您将阅读的 ReadXml(XmlReader)方法中并反序列化Car元素,但当您到达SalesPerson元素时,您将其子树读作字符串,然后使用StreamWriter在文本内容上构建Stream。
如果您不希望XmlSerializer写出SalesPerson元素,请使用[XmlIgnore]属性。当你将Cars类序列化为XML表示时,我不确定你想要发生什么。您是否尝试仅阻止SalesPerson的反序列化,同时仍能序列化Stream所代表的SalesPerson的XML表示形式?
如果你想要一个具体的实现,我可能会提供一个代码示例。
答案 7 :(得分:0)
如果您只想解析SalesPerson元素但将其保留为字符串,则应使用Xsl Transform而不是“Deserialization”。另一方面,如果要解析SalesPerson元素并仅从所有其他非SalesPerson元素填充内存中的对象,那么Xsl Transform也可能是最佳选择。如果文件很大,您可以考虑将它们分开并使用Xsl组合不同的xml文件,以便SalesPerson I / O仅在您需要时才会出现。
答案 8 :(得分:0)
我建议您使用XmlReader,XPathDocument或LINQ-to-XML等任何轻量级方法从Xml手动读取。
当你只需要阅读3个属性时,我想你可以编写从该节点手动读取的代码,并完全控制它的执行方式,而不是依赖于序列化/反序列化