如何将2个元素反序列化为一种类型,然后序列化为仅1个元素

时间:2016-10-17 16:10:24

标签: xml vb.net serialization

我有以下内容:

<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)

如您所见,数组元素PointLineline 反序列化以键入PointLine。正如预期的那样,我得到了一个例外:

Ambiguous types specified for member 'lines'. Items 'line' and 'PointLine' have the same type. Please consider using XmlElementAttribute with XmlChoiceIdentifierAttribute instead.

这里的关键是,当我再次序列化数据时,LinePointLine都应序列化为PointLine

这是为了保持与旧版XML文件的向后兼容性。

我已经做了一些寻找并且已经到了XmlChoiceIdentifierAttribute。在我投入大量时间学习如何使用它之前,它可以做我需要的吗?这是最简单的解决方案吗?如果没有,我怎么能实现这个目标?

1 个答案:

答案 0 :(得分:0)

使用<XmlChoiceIdentifier>可以将两个不同的元素名称映射到多态集合中的相同Type,但是在您的情况下它有点不方便,因为:

  1. XmlChoiceIdentifier似乎只能使用数组,而您使用的是List(Of Lines.Line)。因此,将需要一个代理数组属性。

  2. XmlChoiceIdentifier仅适用于没有外部容器元素(即应用了<XmlElement("line", GetType(Lines.PointLine))>属性)的数组,而您使用XmlArrayArrribute来表示外部容器元素应该使用。因此,您必须将您的代理数组属性嵌套在另一个代理容器中,以获得正确的嵌套级别。

    对于此代理项使用Structure会更容易,因为XmlSerializer只有在完全填充后才会将其设置回容器类型中。如果代理人是Class,它一旦被构造就会被撤回 - 即在实际读入数组之前。

  3. 您还需要创建一个Enum,其元素名称对应于多态集合中的所有可能的元素名称,并创建一个属性,该属性返回与1-1对应的枚举值数组。 Line个对象的数组。

  4. 因此,如果您的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