我有以下内容:
<XmlArray("lines")> _
<XmlArrayItem("CalculatedLine", GetType(Lines.CalculatedLine))> _
<XmlArrayItem("PointLine", GetType(Lines.PointLine))> _
<XmlArrayItem("line", GetType(Lines.PointLine))> _
Public Property lines As List(Of Lines.Line) = New List(Of Lines.Line)
如您所见,数组元素PointLine
和line
应反序列化以键入PointLine
。正如预期的那样,我得到了一个例外:
Ambiguous types specified for member 'lines'. Items 'line' and 'PointLine' have the same type. Please consider using XmlElementAttribute with XmlChoiceIdentifierAttribute instead.
这里的关键是,当我再次序列化数据时,Line
和PointLine
都应序列化为PointLine
。
这是为了保持与旧版XML文件的向后兼容性。
我已经做了一些寻找并且已经到了XmlChoiceIdentifierAttribute
。在我投入大量时间学习如何使用它之前,它可以做我需要的吗?这是最简单的解决方案吗?如果没有,我怎么能实现这个目标?
答案 0 :(得分:0)
使用<XmlChoiceIdentifier>
可以将两个不同的元素名称映射到多态集合中的相同Type
,但是在您的情况下它有点不方便,因为:
XmlChoiceIdentifier
似乎只能使用数组,而您使用的是List(Of Lines.Line)
。因此,将需要一个代理数组属性。
XmlChoiceIdentifier
仅适用于没有外部容器元素(即应用了<XmlElement("line", GetType(Lines.PointLine))>
属性)的数组,而您使用XmlArrayArrribute
来表示外部容器元素应该使用。因此,您必须将您的代理数组属性嵌套在另一个代理容器中,以获得正确的嵌套级别。
对于此代理项使用Structure
会更容易,因为XmlSerializer
只有在完全填充后才会将其设置回容器类型中。如果代理人是Class
,它一旦被构造就会被撤回 - 即在实际读入数组之前。
您还需要创建一个Enum
,其元素名称对应于多态集合中的所有可能的元素名称,并创建一个属性,该属性返回与1-1对应的枚举值数组。 Line
个对象的数组。
因此,如果您的Line
类层次结构定义如下:
Public MustInherit Class Line
Public MustOverride ReadOnly Property LineChoiceType As LineChoiceType
End Class
Public Class CalculatedLine
Inherits Line
Public Overrides ReadOnly Property LineChoiceType As LineChoiceType
Get
Return LineChoiceType.CalculatedLine
End Get
End Property
End Class
Public Class PointLine
Inherits Line
Public Overrides ReadOnly Property LineChoiceType As LineChoiceType
Get
Return LineChoiceType.line
End Get
End Property
End Class
<XmlType(IncludeInSchema := false)> _
Public Enum LineChoiceType
line
CalculatedLine
PointLine
End Enum
请注意,我有Line
的每个具体子类型返回其LineChoiceType
。如果您愿意,可以选择其他方法将类型映射到LineChoiceType
,例如扩展方法。
您可以按照以下格式对属于名为Document
的类的行列表进行序列化和反序列化,如下所示:
Public Class Document
<XmlIgnore> _
Public Property lines As List(Of Lines.Line) = New List(Of Lines.Line)
<XmlElement("lines")> _
Public Property LinesListSurrogate as LinesListSurrogate
Get
Dim surrogate as new LinesListSurrogate
If (lines IsNot Nothing) Then
surrogate.LinesArray = lines.ToArray()
End If
return surrogate
End Get
Set(ByVal Value As LinesListSurrogate)
If Value.LinesArray IsNot Nothing
If lines is Nothing
lines = New List(Of Lines.Line)(Value.LinesArray.Length)
End If
lines.AddRange(Value.LinesArray)
End If
End Set
End Property
End Class
Public Structure LinesListSurrogate
<XmlElement("CalculatedLine", GetType(Lines.CalculatedLine))> _
<XmlElement("PointLine", GetType(Lines.PointLine))> _
<XmlElement("line", GetType(Lines.PointLine))> _
<XmlChoiceIdentifier("LinesChoice")> _
Public Property LinesArray As Lines.Line()
<XmlIgnore> _
Public Property LinesChoice as Lines.LineChoiceType()
Get
If LinesArray Is Nothing Then
Return Nothing
End If
Return LinesArray.Select(Function(l as Lines.Line) If(l Is Nothing, Lines.LineChoiceType.line, l.LineChoiceType)).ToArray()
End Get
Set
' Do nothing - don't care
End Set
End Property
End Structure
此代码可以成功反序列化包含<PointLine>
和<line>
元素的以下XML:
<Document xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<lines>
<CalculatedLine />
<PointLine />
<line />
</lines>
</Document>
仅使用<line>
元素重新序列化它:
<Document xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<lines>
<CalculatedLine />
<line />
<line />
</lines>
</Document>
示例fiddle。