【发布时间】:2017-02-20 11:04:50
【问题描述】:
我最近使用 XMLSerializer 注册了未知节点、元素和属性的事件处理程序,用于从类型层次结构中反序列化复杂类型。我这样做是因为我收到的一些 XML 来自第三方;我对可能给我带来麻烦的数据格式更改感兴趣。
在 XMLSerializer 生成的 XML 中,它使用标准 XML 属性 xsi:type="somederivedtypename" 来识别由 XML 元素表示的实际派生类型。
我惊讶地发现,同一个序列化程序在反序列化时将它刚刚产生的相同属性视为未知。不过有趣的是,反序列化是正确且完整的(在我的实际程序中也有更复杂的类型和数据)。这意味着序列化程序在反序列化的早期阶段会正确评估类型信息。但是在后来的数据提取阶段,该属性显然被误认为是对象的真实数据部分,这当然是未知的。
在我的应用程序中,无端警告最终会弄乱一个不受欢迎的通用日志文件。在我看来,序列化程序应该读回它生成的 XML,而不会出现问题。我的问题:
- 我做错了吗?
- 有解决方法吗?
这里有一个最小的例子:
using System;
using System.IO;
using System.Xml.Serialization;
namespace XsiTypeAnomaly
{
/// <summary>
/// A trivial base type.
/// </summary>
[XmlInclude(typeof(DerivedT))]
public class BaseT{}
/// <summary>
/// A trivial derived type to demonstrate a serialization issue.
/// </summary>
public class DerivedT : BaseT
{
public int anInt { get; set; }
}
class Program
{
private static void serializer_UnknownAttribute
( object sender,
XmlAttributeEventArgs e )
{
Console.Error.WriteLine("Warning: Deserializing "
+ e.ObjectBeingDeserialized
+ ": Unknown attribute "
+ e.Attr.Name);
}
private static void serializer_UnknownNode(object sender, XmlNodeEventArgs e)
{
Console.Error.WriteLine("Warning: Deserializing "
+ e.ObjectBeingDeserialized
+ ": Unknown node "
+ e.Name);
}
private static void serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
Console.Error.WriteLine("Warning: Deserializing "
+ e.ObjectBeingDeserialized
+ ": Unknown element "
+ e.Element.Name);
}
/// <summary>
/// Serialize, display the xml, and deserialize a trivial object.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
BaseT aTypeObj = new DerivedT() { anInt = 1 };
using (MemoryStream stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(BaseT));
// register event handlers for unknown XML bits
serializer.UnknownAttribute += serializer_UnknownAttribute;
serializer.UnknownElement += serializer_UnknownElement;
serializer.UnknownNode += serializer_UnknownNode;
serializer.Serialize(stream, aTypeObj);
stream.Flush();
// output the xml
stream.Position = 0;
Console.Write((new StreamReader(stream)).ReadToEnd() + Environment.NewLine);
stream.Position = 0;
var serResult = serializer.Deserialize(stream) as DerivedT;
Console.WriteLine(
(serResult.anInt == 1 ? "Successfully " : "Unsuccessfully ")
+ "read back object");
}
}
}
}
输出:
<?xml version="1.0"?>
<BaseT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="DerivedT">
<anInt>1</anInt>
</BaseT>
Warning: Deserializing XsiTypeAnomaly.DerivedT: Unknown node xsi:type
Warning: Deserializing XsiTypeAnomaly.DerivedT: Unknown attribute xsi:type
Successfully read back object
【问题讨论】:
-
警告似乎是有道理的,因为您为
BaseT创建了一个序列化程序,然后实际上输入了一个DerivedT对象。如果您只是为DerivedT创建一个序列化程序,警告就会消失。 -
@jstreet 但是这个属性的重点是使基类序列化程序能够反序列化派生对象。想象一个基类列表,它可以包含任何派生类型。调用代码不知道也不关心列表中包含哪些实际派生类型。列表处理代码实际上是在许多派生类型存在之前编写的。
-
我明白你的意思,并且序列化程序能够反序列化派生对象,但警告的“不便”,因为它实际上不知道属性
anInt。建议:当您创建序列化程序时,请使用aTypeObj.GetType(),而不是使用任何显式类型,无论是基类还是派生类。 -
@jstreet 警告与
anInt无关;该元素已正确序列化和反序列化(当我在反序列化后测试非默认值时可以看出)。正如它所说,警告是关于属性xsi:type。属性可以在语法上携带对象信息(即,我可以将anInt序列化为属性!),但被序列化程序用来存储有关类型的元信息。属性xmlns:xsi和xmlns:xsd被正确识别为“不是对象数据的一部分”,但由于某种原因,xsi:type不是。我相信这是一个错误。
标签: c# xml xmlserializer xsi xsitype