【问题标题】:Remove xsi:type from generated xml when serializing序列化时从生成的 xml 中删除 xsi:type
【发布时间】:2011-12-01 04:14:09
【问题描述】:

我正在向外部发送 XML。

其中一个名为“datafield”的节点有一个名为“value”的元素。这可能包含普通的文本内容,或一个 html 内容(我需要将其包装在 CData 中)。

所以,我创建了一个基类(ProvisionDataField),其中继承了 2 个类(ProvisionTextField 和 ProvisionCDataField),如下所示:

<XmlInclude(GetType(ProvisionTextField))>
<XmlInclude(GetType(ProvisionCDataField))>
Public MustInherit Class ProvisionDataField

    <XmlAttribute("datatype")>
    Public Property DataType As String

    <XmlElement("name")>
    Public Property Name As String

End Class

Public Class ProvisionCDataField
    Inherits ProvisionDataField

    <XmlIgnore()>
    Public Property ValueContent As String

    <XmlElement("value")>
    Public Property Value() As XmlCDataSection
        Get
            Dim doc As New XmlDocument
            Return doc.CreateCDataSection(ValueContent)
        End Get
        Set(ByVal value As XmlCDataSection)
            ValueContent = value.Value
        End Set
    End Property
End Class

Public Class ProvisionTextField
    Inherits ProvisionDataField

    <XmlElement("value")>
    Public Property Value As String

End Class

当我序列化时,我得到这样的东西:

   <entitydata entitytype="company">
      <datafield xsi:type="ProvisionTextField" datatype="string">
        <name>companyAcronym</name>
        <value>testCompany</value>
      </datafield>
      <datafield xsi:type="ProvisionCDataField" datatype="string">
        <name>ssocontent</name>
        <value><![CDATA[<html><body> HTML Content</body></html>]]></value>
      </datafield>
    </entitydata>

除了被告知我必须从 xml 中删除“xsi:type”之外,一切都很好。因此,我需要生成的 xml 看起来像这样:

   <entitydata entitytype="company">
      <datafield datatype="string">
        <name>companyAcronym</name>
        <value>testCompany</value>
      </datafield>
      <datafield datatype="string">
        <name>ssocontent</name>
        <value><![CDATA[<html><body> HTML Content</body></html>]]></value>
      </datafield>
    </entitydata>

这可能吗?

【问题讨论】:

  • 没有xsi:type,XML 的接收者如何知道存在哪种类型?
  • 只是为了提供更多信息,我正在使用 POX(普通旧 XML)与外部服务(用 Java 编写)进行通信。我没有手动编写 xml,而是创建了类并使用 xml 序列化来生成 xml。 “类型”只是我使用继承的结果。另一个系统根本不知道这种类型,也不想要它。

标签: .net xml vb.net xml-serialization cdata


【解决方案1】:

这是我正在寻找的答案 - 它将确保省略继承中使用的 XmlInclude 属性产生的 xsi:type:

    ElseIf ns = XmlSchema.InstanceNamespace Then
        ' Omits all XSI attributes
        _skip = True
        Return
    End If

虽然本节将省略根目录中的 xmlns:xsd 和 xmlns:xsi

    If prefix = "xmlns" AndAlso (localName = "xsd" OrElse localName = "xsi") Then
        ' Omits XSD and XSI from root
        _skip = True
        Return

完整代码:

Imports System.IO
Imports System.Xml
Imports System.Xml.Schema

Public Class PlainXmlTextWriter
    Inherits XmlTextWriter

    Public Sub New(ByVal w As TextWriter)
        MyBase.new(w)
    End Sub

    Public Sub New(ByVal w As Stream, ByVal encoding As Encoding)
        MyBase.new(w, encoding)
    End Sub

    Public Sub New(ByVal filename As String, ByVal encoding As Encoding)
        MyBase.new(filename, encoding)
    End Sub

    Dim _skip As Boolean = False

    Public Overrides Sub WriteStartAttribute(ByVal prefix As String, ByVal localName As String, ByVal ns As String)
        If prefix = "xmlns" AndAlso (localName = "xsd" OrElse localName = "xsi") Then
            ' Omits XSD and XSI from root
            _skip = True
            Return
        ElseIf ns = XmlSchema.InstanceNamespace Then
            ' Omits all XSI attributes
            _skip = True
            Return
        End If
        MyBase.WriteStartAttribute(prefix, localName, ns)
    End Sub

    Public Overrides Sub WriteString(ByVal text As String)
        If _skip Then Return
        MyBase.WriteString(text)
    End Sub

    Public Overrides Sub WriteEndAttribute()
        If _skip Then
            _skip = False
            Return
        End If
        MyBase.WriteEndAttribute()
    End Sub
End Class

【讨论】:

    【解决方案2】:

    无需重写 XmlWriter,只需使用 XmlSerializerNamespace 的实例即可:

    Sub Main()
        Dim xSer As New XmlSerializer(GetType(MyType))
    
        Dim sb As New StringBuilder()
    
        Dim obj As MyType = getAnInstanceOfMyType()
    
        Using wrt As New StringWriter(sb)
            Dim ns As New XmlSerializerNamespaces
            ns.Add("", "")
    
            xSer.Serialize(wrt, obj, ns)
    
        End Using
    
        Console.WriteLine(sb.ToString())
    
        Console.ReadLine()
    End Sub
    

    这将导致 xml 完全没有命名空间。

    编辑:更改为 VB 代码

    编辑 2:经过进一步测试,我使用的测试代码仅从生成的 xml 中删除了命名空间声明。即使我使用了 OP 提供的类,我的原始测试也没有在元素上产生 xsi:type 属性,所以我无法确定我发布的代码是否会删除它们,正如 John Saunder 在 cmets 中提到的那样。我推测如果命名空间被移除,那么 xsi:type 属性也会被移除,但我发布的代码并不能证明这一点。

    【讨论】:

    • 我在发布之前测试了代码,它似乎可以工作,xsi类型被删除了。
    【解决方案3】:

    您将不得不覆盖 xmlwriter。

    This blogpost(不是我的)告诉你怎么做。

    这是 VB.Net 版本。

    Imports System.Xml.Serialization
    Imports System.Xml
    Imports System.IO
    Imports System.Text
    
    Module Module1
    
        Sub Main()
            Dim p As New ProvisionCDataField()
            p.Name = "test"
            Dim sw1 = New StringWriter()
            Dim xs1 As New XmlSerializer(GetType(ProvisionDataField))
            xs1.Serialize(New XmlTextWriter(sw1), p)
            Console.WriteLine(sw1.ToString())
            Dim sw2 = New StringWriter()
            Dim xs2 As New XmlSerializer(GetType(ProvisionDataField))
            xs2.Serialize(New NonXsiTextWriter(sw2), p)
            Console.WriteLine(sw2.ToString())
            Console.ReadLine()
        End Sub
    
    End Module
    
    Public Class NonXsiTextWriter
        Inherits XmlTextWriter
    
        Public Sub New(ByVal w As TextWriter)
            MyBase.new(w)
        End Sub
    
        Public Sub New(ByVal w As Stream, ByVal encoding As Encoding)
            MyBase.new(w, encoding)
        End Sub
    
        Public Sub New(ByVal filename As String, ByVal encoding As Encoding)
            MyBase.new(filename, encoding)
        End Sub
    
        Dim _skip As Boolean = False
    
        Public Overrides Sub WriteStartAttribute(ByVal prefix As String, ByVal localName As String, ByVal ns As String)
            If localName = "xsi" Then
                _skip = True
                Return
            End If
            MyBase.WriteStartAttribute(prefix, localName, ns)
        End Sub
    
        Public Overrides Sub WriteString(ByVal text As String)
            If _skip Then Return
            MyBase.WriteString(text)
        End Sub
    
        Public Overrides Sub WriteEndAttribute()
            If _skip Then
                _skip = False
                Return
            End If
            MyBase.WriteEndAttribute()
        End Sub
    End Class
    
    <XmlInclude(GetType(ProvisionTextField))>
    <XmlInclude(GetType(ProvisionCDataField))>
    Public MustInherit Class ProvisionDataField
    
        <XmlAttribute("datatype")>
        Public Property DataType As String
    
        <XmlElement("name")>
        Public Property Name As String
    
    End Class
    
    Public Class ProvisionCDataField
        Inherits ProvisionDataField
    
        <XmlIgnore()>
        Public Property ValueContent As String
    
        <XmlElement("value")>
        Public Property Value() As XmlCDataSection
            Get
                Dim doc As New XmlDocument
                Return doc.CreateCDataSection(ValueContent)
            End Get
            Set(ByVal value As XmlCDataSection)
                ValueContent = value.Value
            End Set
        End Property
    End Class
    
    Public Class ProvisionTextField
        Inherits ProvisionDataField
    
        <XmlElement("value")>
        Public Property Value As String
    
    End Class
    

    结果就是这样。

    <?xml version="1.0" encoding="utf-16"?>
     <ProvisionDataField xmlns:xsi="http://www .w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="ProvisionCDataField">
       <name>test</name><value><![CDATA[]]></value>
     </ProvisionDataField> 
    
    <?xml version="1.0" encoding="utf-16"?>
     <ProvisionDataField xmlns:xsd="http://www.w3.org/2001/XMLSchema" d1p1:type="ProvisionCDataField"  xmlns:d1p1="http://www.w3.org/2001/XMLSchema-instance">
      <name>test</name><value><![CDATA[]]></value>
     </ProvisionDataField>
    

    【讨论】:

    • 你的结果仍然有 OP 想要摆脱的命名空间
    • -1 用于展示如何使用 new XmlTextWriter() 的示例,自 .NET 2.0 起已弃用。
    • @John 它没有被弃用,但 xmlwriter.create 现在是首选的做事方式msdn.microsoft.com/en-us/library/system.xml.xmltextwriter.aspx
    • 这是一个非常有力的主张,约翰。我相信 ObsoleteAttribute 是专门引入的,以将事物标记为已弃用(更不用说已弃用的成员列表 msdn.microsoft.com/en-us/library/ee471421.aspx)。不太推荐!= 已弃用。
    • @JohnSaunders 它甚至还在 4.5 框架中,对我来说已弃用意味着他们将在下一个版本中删除它,他们显然没有这样做。
    猜你喜欢
    • 2017-01-23
    • 2015-10-24
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多