11

I got thousands of XML files following all the same schema/structure. I implemented IXmlSerializable and thus am reading the elements and attributes myself.

My problem is that these files each use a different phony namespace. These files come from an other source so I cannot change that :D Also, there are too many of those namespaces for me to just build an array of the possible namespaces and pass it along to the xmlserializer.

Right now, if I don't specify a namespace, it throws a [xmlns:ns0="http://tempuri.org/abcd.xsd" was not expected] error.

I would like to be able to tell the serializer to simply ignore the namespace when deserializing my object and just fire ReadXML. Or just be able to tell it to accept any "http://tempuri.org/" namespace.

Is that possible?

I would like to avoid modifying the files as much as possible.

Thank you!

4

4 回答 4

2

对的,这是可能的。当你调用你的Deserialize方法时XmlSerializer,你可以指定一个XmlTextReader实例。

Cheeso 在相关 C# 问题上的这个答案显示了如何创建一个XmlTextReader忽略 XML 文件中出现的任何命名空间的方法。我冒昧地将他的想法翻译成 VB,并根据您的要求创建了一个简单的概念验证示例:

Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Xml.Serialization

' Helper class
Class NamespaceIgnorantXmlTextReader
    Inherits XmlTextReader

    Public Sub New(stream As Stream)
        MyBase.New(stream)
    End Sub

    Public Overrides ReadOnly Property NamespaceURI As String
        Get
            Return ""
        End Get
    End Property
End Class

' Serializable class
Public Class ExampleClass
    Public Property MyProperty As String
End Class

' Example
Module Module1
    Sub Main()
        Dim testXmlStream = New MemoryStream(Encoding.UTF8.GetBytes(
            "<ExampleClass xmlns=""http://tempuri.org/SomePhonyNamespace1.xsd"" 
                           xmlns:ph2=""http://tempuri.org/SomePhonyNamespace2.xsd"">
                 <ph2:MyProperty>SomeValue</ph2:MyProperty>
             </ExampleClass>"))

        Dim serializer As New XmlSerializer(GetType(ExampleClass))
        Dim reader As New NamespaceIgnorantXmlTextReader(testXmlStream)
        Dim example = DirectCast(serializer.Deserialize(reader), ExampleClass)

        Console.WriteLine(example.MyProperty)   ' prints SomeValue
    End Sub
End Module

注意:如果只是文档的默认命名空间不同(即各个标签没有不同的命名空间),TextXmlReader则使用Namespaces属性设置为的标准False就足够了。

Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Xml.Serialization

' Serializable Class
Public Class ExampleClass
    Public Property MyProperty As String
End Class

' Example
Module Module1
    Sub Main()
        Dim testXmlStream = New MemoryStream(Encoding.UTF8.GetBytes(
            "<ExampleClass xmlns=""http://tempuri.org/SomePhonyNamespace1.xsd"">
                 <MyProperty>SomeValue</MyProperty>
             </ExampleClass>"))

        Dim serializer As New XmlSerializer(GetType(ExampleClass))
        Dim reader As New XmlTextReader(testXmlStream)
        reader.Namespaces = False
        Dim example = DirectCast(serializer.Deserialize(reader), ExampleClass)

        Console.WriteLine(example.MyProperty)   ' prints SomeValue
    End Sub
End Module
于 2016-10-13T21:09:36.477 回答
0

这不是您关于如何告诉 XmlSerialiser 忽略名称空间的问题的答案,而是一种解决方法。在序列化之前,您可以使用 xslt 转换从 xml 中剥离名称空间。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/|comment()|processing-instruction()">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

几个扩展方法作为这个的助手,可能会有点棘手,但我会尝试:

/// <summary>
/// Transforms the xmldocument to remove all namespaces using xslt
/// http://stackoverflow.com/questions/987135/how-to-remove-all-namespaces-from-xml-with-c
/// http://msdn.microsoft.com/en-us/library/42d26t30.aspx
/// </summary>
/// <param name="xmlDocument"></param>
/// <param name="indent"></param>
public static XmlDocument RemoveXmlNameSpaces(this XmlDocument xmlDocument, bool indent = true)
{
    return xmlDocument.ApplyXsltTransform(Properties.Resources.RemoveNamespaces, indent);
}

public static XmlDocument ApplyXsltTransform(this XmlDocument xmlDocument, string xsltString,bool indent= true)
{
    var xslCompiledTransform = new XslCompiledTransform();
    Encoding encoding;
    if (xmlDocument.GetEncoding() == null)
    {
        encoding = DefaultEncoding;
    }
    else
    {
        encoding = Encoding.GetEncoding(xmlDocument.GetXmlDeclaration().Encoding);
    }
    using (var xmlTextReader = xsltString.GetXmlTextReader())
    {
        xslCompiledTransform.Load(xmlTextReader);
    }
    XPathDocument xPathDocument = null;
    using (XmlTextReader xmlTextReader = xmlDocument.OuterXml.GetXmlTextReader())
    {
        xPathDocument = new XPathDocument(xmlTextReader);
    }
    using (var memoryStream = new MemoryStream())
    {
        using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, new XmlWriterSettings()
            {
                Encoding = encoding,
                Indent = indent
            }))
        {
            xslCompiledTransform.Transform(xPathDocument, xmlWriter);
        }
        memoryStream.Position = 0;
        using (var streamReader = new StreamReader(memoryStream, encoding))
        {
            string readToEnd = streamReader.ReadToEnd();
            return readToEnd.ToXmlDocument();
        }
    }
}

public static Encoding GetEncoding(this XmlDocument xmlDocument)
{
    XmlDeclaration xmlDeclaration = xmlDocument.GetXmlDeclaration();
    if (xmlDeclaration == null)
        return null;
    return Encoding.GetEncoding(xmlDeclaration.Encoding);
}

public static XmlDeclaration GetXmlDeclaration(this XmlDocument xmlDocument)
{
    XmlDeclaration xmlDeclaration = null;
    if (xmlDocument.HasChildNodes)
        xmlDeclaration = xmlDocument.FirstChild as XmlDeclaration;
    return xmlDeclaration;
}

public static XmlTextReader GetXmlTextReader(this string xml)
{
    return new XmlTextReader(new StringReader(xml));
}
于 2012-10-11T16:15:02.867 回答
0

为了解释 Heinzi 的回答,我需要更改默认命名空间(从技术上讲,是根元素的命名空间),这样我就可以使用XmlAttributeOverrides应用于我无法控制的类层次结构来反序列化文档。作为其中的一部分,我必须为XmlRootAttribute第一类分配一个属性。问题在于,为了反序列化文档,XmlSerializer期望 Namespace 值与 的命名空间匹配XmlRootAttribute,这是无法保证的。

使用以下派生自 的类,XmlReader可以为反序列化程序为根元素的命名空间分配一个已知值。可以强制读取器的命名空间与XmlRootAttribute属性的命名空间匹配(即使它是空字符串)。

简化解决方案是使用XmlWrappingReaderAlterant 在 StackOverflow 上的回答到How do I create a XmlTextReader that ignore Namespaces and does not check characters

/// <summary>
/// XML document reader replaces the namespace of the root element.
/// </summary>
public class MyXmlReader : Mvp.Xml.Common.XmlWrappingReader
{
    // Namespace of the document's root element. Read from document.
    private string rootNamespace = "";

    /// <summary>
    /// Get or set the target namespace to use when deserializing.
    /// </summary>
    public string TargetNamespace { get; set; }

    /// <summary>
    /// Initialize a new instance of the MXmlReader class.
    /// </summary>
    /// <param name="reader">XmlReader instance to modify.</param>
    public MyXmlReader(XmlReader reader) : base(reader)
    {
        TargetNamespace = "";
    }

    /// <summary>
    /// Return the namespace of the XML node. Substitute the target namespace if it matches the namespace of the root element.
    /// </summary>
    public override string NamespaceURI
    {
        get
        {
            if (Depth == 0 && NodeType == XmlNodeType.Element)
            {
                // Save the namespace from the document's root element.
                rootNamespace = base.NamespaceURI;
            }

            if (base.NamespaceURI == rootNamespace)
            {
                // Substitute the schema's targetNamespace for the root namespace.
                return TargetNamespace;
            }

            // Use the native namespace of the XML node.
            return base.NamespaceURI;
        }
    }
}

我实例化了 MyXmlReader 并使用它反序列化到标记为的对象XmlRootAttribute(ElementName = "DocumentRoot", Namespace = "http://my.target.namespace")

var reader = new MyXmlReader(XmlReader.Create(stream));
reader.TargetNamespace = "http://my.target.namespace";

// Deserialize using the defined XML attribute overrides that can
// supply XML serialization attributes to types at runtime.
Type t = typeof(SomeDeserializedObject);
var xo = SomeDeserializedObject.GetXmlAttributeOverrides();
XmlSerializer serializer = new XmlSerializer(t, xo);
SomeDeserializedObject o = (SomeDeserializedObject)serializer.Deserialize(reader);

如果我导入的 XML 文档有不同的根命名空间,或者根本没有指定一个,现在我仍然可以反序列化它。

于 2020-03-12T19:43:16.090 回答
-1

您可以使用此代码从 xml 文件中删除名称空间

using (FileStream stream = new FileStream("FilePath",FileMode.Create))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(YourClass));
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                    ns.Add("", "");                    
                    serializer.Serialize(stream," Your Object to Serialize",ns);
                }
于 2015-02-04T20:03:58.277 回答