简体   繁体   English

C#反序列化xml而忽略命名空间

[英]C# deserialize xml while ignoring namespace

I have to load and deserialize an Xml file into an object.我必须加载一个 Xml 文件并将其反序列化为一个对象。 I can read the xml, get to the point where the object is described and parse the xml only from that part which is great, but there is a namespace declared in the root of the xml.我可以读取 xml,到达描述对象的地步,并仅从那部分解析 xml,这很好,但是在 xml 的根中声明了一个命名空间。

I don't understand why but when reading the xml, even though I read it from a given node, the xmlns attribute gets added to it, resulting in my program not being able to deserialize that into an object, due to the unexpected member.我不明白为什么,但是在读取 xml 时,即使我从给定节点读取它,也会将 xmlns 属性添加到其中,导致我的程序无法将其反序列化为对象,这是由于意外成员。

My code:我的代码:

public static SomeClass GetObjectFromXml (string path)
    {
        XmlReader reader = XmlReader.Create(path);
        string wantedNodeContents = string.Empty;
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "IWantThis")
            {
                wantedNodeContents = reader.ReadOuterXml();
                break;
            }
        }
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(SomeClass));
        System.IO.StringReader stringReader = new System.IO.StringReader(wantedNodeContents);
        SomeClass loadedSomeClassXml = xmlSerializer.Deserialize(stringReader) as SomeClass;
        return loadedSomeClassXml;
    }

How could I get rid of the xmlns and deserialize the xml into an object?我怎样才能摆脱 xmlns 并将 xml 反序列化为一个对象?

XDocument provides you a bit of more flexibility at time of deserialize any XML. XDocument 在反序列化任何 XML 时为您提供了更多的灵活性。 I had a similiar problem and it was resolve using the next snippet code:我有一个类似的问题,使用下一个片段代码解决了这个问题:

///Type T must have a default constructor

private T XMLToObject (string pathXML)
{
   T myObjectParsedFromXML= default(T);

   LoadOptions loadOpt = LoadOptions.SetLineInfo;
   XDocument xmlDocument = XDocument.Load(pathXML , loadOpt);

   string namespaceXML = xmlDocument.Root.Name.Namespace.NamespaceName;
   XmlSerializer serializer = new XmlSerializer(typeof(T), defaultNamespace: namespaceXML); 
   
   XmlReader XMLreader = xmlDocument.CreateReader();

   myObjectParsedFromXML= (T)serializer.Deserialize(XMLreader);   
   
   return myObjectParsedFromXML;
}

In addition, XmlSerializer provides you a set of events for register any issue or error during serialization process:此外,XmlSerializer 为您提供了一组事件,用于在序列化过程中注册任何问题或错误:

 XmlSerializer serializer = new XmlSerializer(typeof(T), defaultNamespace: namespaceXML);
 
 serializer.UnknownAttribute += new XmlAttributeEventHandler((sender, args) =>
            {
                //Your code for manage the errors during serialization
            });

 serializer.UnknownElement += new XmlElementEventHandler((sender, args) =>
            {  
               //Your code for manage the errors during serialization  
            });

You have a few issues here:你在这里有几个问题:

  1. The default namespace attribute is added to the string returned by ReadOuterXml() because ReadOuterXml() is designed not to change the semantics of the returned XML .默认命名空间属性被添加到ReadOuterXml()返回的字符串中,因为ReadOuterXml()旨在不更改返回的 XML 的语义 Apparently in your XML there is a default namespace applied to some parent node of <IWantThis> -- which, being a default namespace, recursively applies to <IWantThis> itself.显然,在您的 XML 中,有一个默认命名空间应用于<IWantThis>某个父节点 - 作为默认命名空间,它递归地应用于<IWantThis>本身。 To retain this namespace membership, ReadOuterXml() must emit a default namespace as it writes out the nested XML.为了保留这个命名空间成员资格, ReadOuterXml()在写出嵌套的 XML 时必须发出一个默认命名空间。

    If you really want to completely ignore namespaces on XML, you need to create a custom XmlReader , eg as shown in如果你真的想完全忽略 XML 上的命名空间,你需要创建一个自定义的XmlReader ,例如

  2. You need to construct an XmlSerializer for SomeClass whose expected root node is <IWantThis> .您需要为其预期根节点为<IWantThis> SomeClass构造一个XmlSerializer You can do this using the XmlSerializer(Type, XmlRootAttribute) constructor, however, if you do, you must statically cache and reuse the serializer to avoid a severe memory leak , as explained in Memory Leak using StreamReader and XmlSerializer .您可以使用XmlSerializer(Type, XmlRootAttribute)构造函数执行此操作,但是,如果您这样做,则必须静态缓存XmlSerializer(Type, XmlRootAttribute)用序列化程序以避免严重的内存泄漏,如使用 StreamReader 和 XmlSerializer 的内存泄漏中所述

  3. You are creating a local copy wantedNodeContents of the element you want to deserialize, then re-parsing that local copy.您正在创建要反序列化的元素的本地副本wantedNodeContents ,然后重新解析该本地副本。 There is no need to do this, you can use XmlReader.ReadSubtree() to deserialize just a portion of the XML.没有必要这样做,您可以使用XmlReader.ReadSubtree()反序列化 XML 的一部分。

Putting all these issues together, your GetObjectFromXml() could look like:将所有这些问题放在一起,您的GetObjectFromXml()可能如下所示:

public static partial class XmlExtensions
{
    public static T GetObjectFromXml<T>(string path, string localName, string namespaceURI, bool ignoreNamespaces = false)
    {
        using (var textReader = new StreamReader(path))
            return GetObjectFromXml<T>(textReader, localName, namespaceURI);
    }
    
    public static T GetObjectFromXml<T>(TextReader textReader, string localName, string namespaceURI, bool ignoreNamespaces = false)
    {
        using (var xmlReader = ignoreNamespaces ? new NamespaceIgnorantXmlTextReader(textReader) : XmlReader.Create(textReader))
            return GetObjectFromXml<T>(xmlReader, localName, namespaceURI);
    }
    
    public static T GetObjectFromXml<T>(XmlReader reader, string localName, string namespaceURI)
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "IWantThis" && reader.NamespaceURI == namespaceURI)
            {
                var serializer = XmlSerializerFactory.Create(typeof(T), localName, namespaceURI);
                using (var subReader = reader.ReadSubtree())
                    return (T)serializer.Deserialize(subReader);
            }
        }
        // Or throw an exception?
        return default(T);
    }
}

// This class copied from this answer https://stackoverflow.com/a/873281/3744182
// To https://stackoverflow.com/questions/870293/can-i-make-xmlserializer-ignore-the-namespace-on-deserialization
// By https://stackoverflow.com/users/48082/cheeso
// helper class to ignore namespaces when de-serializing
public class NamespaceIgnorantXmlTextReader : XmlTextReader
{
    public NamespaceIgnorantXmlTextReader(System.IO.TextReader reader): base(reader) { }

    public override string NamespaceURI { get { return ""; } }
}

public static class XmlSerializerFactory
{
    // To avoid a memory leak the serializer must be cached.
    // https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
    // This factory taken from 
    // https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648

    readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
    readonly static object padlock;

    static XmlSerializerFactory()
    {
        padlock = new object();
        cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
    }

    public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
    {
        if (serializedType == null)
            throw new ArgumentNullException();
        if (rootName == null && rootNamespace == null)
            return new XmlSerializer(serializedType);
        lock (padlock)
        {
            XmlSerializer serializer;
            var key = Tuple.Create(serializedType, rootName, rootNamespace);
            if (!cache.TryGetValue(key, out serializer))
            {
                cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
            }
            return serializer;
        }
    }
}

Demo fiddle here .演示小提琴在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM