C# 反序列化 xml 同时忽略命名空间
C# deserialize xml while ignoring namespace
我必须将 Xml 文件加载并反序列化为一个对象。我可以读取 xml,到达描述对象的位置并仅从很棒的部分解析 xml,但是在 [=21= 的根目录中声明了一个命名空间].
我不明白为什么,但是在读取 xml 时,即使我从给定节点读取它,xmlns 属性也会添加到它,导致我的程序不被由于意外成员,能够将其反序列化为对象。
我的代码:
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;
}
如何摆脱 xmlns 并将 xml 反序列化为对象?
XDocument 在反序列化任何 XML 时为您提供了更多的灵活性。我有一个类似的问题,使用下一个代码片段解决了这个问题:
///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;
}
此外,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
});
这里有几个问题:
默认命名空间属性被添加到ReadOuterXml()
返回的字符串中,因为ReadOuterXml()
旨在不改变返回的[=63]的语义=]。显然在您的 XML 中有一个 default namespace 应用于 <IWantThis>
的某个父节点——作为默认名称空间,它递归地应用于 <IWantThis>
本身。要保留此命名空间成员资格,ReadOuterXml()
必须在写出嵌套的 XML.
时发出默认命名空间
如果你真的想完全忽略 XML 上的命名空间,你需要创建一个自定义的 XmlReader
,例如如图
您需要为SomeClass
构造一个XmlSerializer
,其预期根节点为<IWantThis>
。您可以使用 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.
您正在创建要反序列化的元素的本地副本 wantedNodeContents
,然后 re-parsing 该本地副本。没有必要这样做,您可以使用 XmlReader.ReadSubtree()
来反序列化 XML.
的一部分
将所有这些问题放在一起,您的 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
// To
// By https://whosebug.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.
//
// This factory taken from
//
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;
}
}
}
演示 fiddle here.
我必须将 Xml 文件加载并反序列化为一个对象。我可以读取 xml,到达描述对象的位置并仅从很棒的部分解析 xml,但是在 [=21= 的根目录中声明了一个命名空间].
我不明白为什么,但是在读取 xml 时,即使我从给定节点读取它,xmlns 属性也会添加到它,导致我的程序不被由于意外成员,能够将其反序列化为对象。
我的代码:
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;
}
如何摆脱 xmlns 并将 xml 反序列化为对象?
XDocument 在反序列化任何 XML 时为您提供了更多的灵活性。我有一个类似的问题,使用下一个代码片段解决了这个问题:
///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;
}
此外,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
});
这里有几个问题:
默认命名空间属性被添加到
时发出默认命名空间ReadOuterXml()
返回的字符串中,因为ReadOuterXml()
旨在不改变返回的[=63]的语义=]。显然在您的 XML 中有一个 default namespace 应用于<IWantThis>
的某个父节点——作为默认名称空间,它递归地应用于<IWantThis>
本身。要保留此命名空间成员资格,ReadOuterXml()
必须在写出嵌套的 XML.如果你真的想完全忽略 XML 上的命名空间,你需要创建一个自定义的
XmlReader
,例如如图您需要为
SomeClass
构造一个XmlSerializer
,其预期根节点为<IWantThis>
。您可以使用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.您正在创建要反序列化的元素的本地副本
的一部分wantedNodeContents
,然后 re-parsing 该本地副本。没有必要这样做,您可以使用XmlReader.ReadSubtree()
来反序列化 XML.
将所有这些问题放在一起,您的 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
// To
// By https://whosebug.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.
//
// This factory taken from
//
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;
}
}
}
演示 fiddle here.