.Net 将名称空间添加到 XML 文档作为默认名称并带有前缀

.Net Add namespace to XML document as default and with a prefix

当使用 XMLSerializer 创建一个 class oXML in vb.net 序列化的 XML 字符串时,如下所示:

Dim x As New Xml.Serialization.XmlSerializer(oXML.GetType, "urn:oecd:blah:blah")
Dim xmlns = New XmlSerializerNamespaces()

xmlns.Add(String.Empty, "urn:oecd:blah:blah")
xmlns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance")
xmlns.Add("sfa", "urn:oecd:blah:blah1")
xmlns.Add("iso", "urn:oecd:blah:blah2")
xmlns.Add("ftc", "urn:oecd:blah:blah")

Dim sw As New IO.StringWriter()
x.Serialize(sw, oXML, xmlns)

您会注意到我重复添加了名称空间 "urn:oecd:blah:blah" 作为 xmlns 对象上的空 "default" 名称空间以及 x As New Xml.Serialization.XmlSerializer 的定义。

问题是这个特定的命名空间只会使用 ftc 前缀呈现,而不会显示默认值。如果我注释掉添加的 ftc 命名空间,然后再次 运行 它,则默认命名空间会正确呈现。有没有办法告诉序列化程序这个命名空间被用作默认名称空间并且还带有 ftc 前缀?我假设这是因为名称空间被使用了两次,既作为默认名称又带有忽略它的前缀?

XmlSerializer 在为相同的命名空间传递带有两个前缀的 XmlSerializerNamespaces 的情况下没有定义的行为。它可以选择任何一个前缀,结果在语义上是相同的。此外,从 class 的 reference source 看来,名称空间存储在散列 table 中,因此它们的顺序是不可预测的 table。但由于 XML 两种方式都具有相同的含义,所以最简单的做法就是不要担心它。

如果出于某种原因您必须在您的XML中有重复的命名空间,并且必须优先使用默认前缀到该命名空间中元素的等效命名前缀,您可以序列化为 XDocument 然后手动添加缺少的重复项:

Public Module XObjectExtensions
    <System.Runtime.CompilerServices.Extension> 
    Public Function SerializeToXDocument(Of T)(obj As T, serializer As XmlSerializer, ns As XmlSerializerNamespaces) As XDocument
        Dim doc = New XDocument()
        Using writer = doc.CreateWriter()
            serializer = If(serializer, New XmlSerializer(obj.GetType()))
            serializer.Serialize(writer, obj, ns)
        End Using
        If doc.Root IsNot Nothing AndAlso ns IsNot Nothing Then
            ' Add missing namespaces
            For Each name In ns.ToArray().Except(doc.Root.Attributes().Where(Function(a) a.IsNamespaceDeclaration).Select((Function(a) New XmlQualifiedName(a.Name.LocalName, a.Value))))
                doc.Root.Add(New XAttribute("xmlns" + (If(String.IsNullOrEmpty(name.Name), String.Empty, ":" + name.Name)), name.Namespace))
            Next
        End If

        Return doc
    End Function
End Module

然后像这样使用它:

        Dim xDoc = oXML.SerializeToXDocument(x, xmlns)

        Dim xml2 as String
        Using sw = New IO.StringWriter()
            xDoc.Save(sw)
            xml2 = sw.ToString()
        End Using

示例dotnetfiddle

现在,在您的情况下,省略了默认命名空间,取而代之的是重复的前缀命名空间。如果出于某种原因 XmlSerializer 选择了相反的做法(并且没有记录它会选择哪个),那么在根文档的命名空间列表的末尾添加缺少的命名空间将导致所有元素都被写入命名空间字首。那是因为事实证明,在编写 XElementname/namespace pairs are pushed onto a stack during serialization and so the lattermost is used. To work around this, you could adapt the code from 并将默认前缀移动到属性列表的 end 而不是 开始.