Deserialize XML without namespaces but in a class expecting namespaces
Duplicate:
Omitting all xml namespaces when serializing an object? Not the same.. I want in the other way: Deserialize!
I have a C# class as bellow:
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.portalfiscal.inf.br/nfe")]
[System.Xml.Serialization.XmlRootAttribute("NFe", Namespace = "http://www.portalfiscal.inf.br/nfe", IsNullable = false)]
public partial class TNFe
{
private TNFeInfNFe infNFeField;
private SignatureType signatureField;
/// <remarks/>
public TNFeInfNFe infNFe
{ ...
I use this class to serialize/deserialize XML files by user request. But I've got a problem: namespaces definition were added on the new version of this software. The XML is still the same, only adding namespaces definition.
Eg., last version...
<?xml version="1.0" encoding="utf-8" ?>
<NFe>
<infNFe version="1.10">
...
and new version...
<?xml version="1.0" encoding="utf-8" ?>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe version="2.10">
...
I need to load XML files with and without these namespaces. I have a lot of nested classes and each of it has its own namespaces definition.
I would like to use the same classes for both XML, with and without 开发者_如何学Pythonnamespaces.
I tried to create an XmlTextReader and overwrite the NamespaceURI method, but I still receives an exception with no much info. I think .NET engine is trying to force the class namespace definition against the XML.
I had run into a similar challenge with a proxy class. For reasons that I won't go into, I needed to serialize the class manually using the XmlSerializer on web server and deserialize on client. I was not able to find an elegant solution online, so I just avoided the issue by removing the XmlTypeAttribute from the proxy class manually after I auto-generated it in Visual Studio.
I kept coming back to see if there was a way to get the namespace to workout. Here is how I got it working without the need to modify the auto-generated classes. I ended up using an XmlTextReader to return the desired namespace on nodes matching a property name. There is room for improvement, but i hope it helps someone.
class Program
{
static void Main(string[] args)
{
//create list to serialize
Person personA = new Person() { Name = "Bob", Age = 10, StartDate = DateTime.Parse("1/1/1960"), Money = 123456m };
List<Person> listA = new List<Person>();
for (int i = 0; i < 10; i++)
{
listA.Add(personA);
}
//serialize list to file
XmlSerializer serializer = new XmlSerializer(typeof(List<Person>));
XmlTextWriter writer = new XmlTextWriter("Test.xml", Encoding.UTF8);
serializer.Serialize(writer, listA);
writer.Close();
//deserialize list from file
serializer = new XmlSerializer(typeof(List<ProxysNamespace.Person>));
List<ProxysNamespace.Person> listB;
using (FileStream file = new FileStream("Test.xml", FileMode.Open))
{
//configure proxy reader
XmlSoapProxyReader reader = new XmlSoapProxyReader(file);
reader.ProxyNamespace = "http://myappns.com/"; //the namespace of the XmlTypeAttribute
reader.ProxyType = typeof(ProxysNamespace.Person); //the type with the XmlTypeAttribute
//deserialize
listB = (List<ProxysNamespace.Person>)serializer.Deserialize(reader);
}
//display list
foreach (ProxysNamespace.Person p in listB)
{
Console.WriteLine(p.ToString());
}
Console.ReadLine();
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime StartDate { get; set; }
public decimal Money { get; set; }
}
namespace ProxysNamespace
{
[XmlTypeAttribute(Namespace = "http://myappns.com/")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime Birthday { get; set; }
public decimal Money { get; set; }
public override string ToString()
{
return string.Format("{0}:{1},{2:d}:{3:c2}", Name, Age, Birthday, Money);
}
}
}
public class XmlSoapProxyReader : XmlTextReader
{
List<object> propNames;
public XmlSoapProxyReader(Stream input)
: base(input)
{
propNames = new List<object>();
}
public string ProxyNamespace { get; set; }
private Type proxyType;
public Type ProxyType
{
get { return proxyType; }
set
{
proxyType = value;
PropertyInfo[] properties = proxyType.GetProperties();
foreach (PropertyInfo p in properties)
{
propNames.Add(p.Name);
}
}
}
public override string NamespaceURI
{
get
{
object localname = LocalName;
if (propNames.Contains(localname))
return ProxyNamespace;
else
return string.Empty;
}
}
}
In order to deserialize XML without namespace add XmlRoot attribute to the class that represents the top of your hierarchy:
[XmlRoot(ElementName="plugins", Namespace="")]
In my example the Xml has no namespace and starts like
<plugins><...
The class that it deserializes to:
[XmlRoot(ElementName="plugins", Namespace="")]
public class Plugins
{
//...
}
Obviously, your situation may be a slightly different, but this code works for me:
using (FileStream stream = File.Open(filePath, FileMode.Open))
{
XmlReader reader = new XmlTextReader(stream);
XmlSerializer serializer = new XmlSerializer(typeof(Plugins));
var plugins = (Plugins)serializer.Deserialize(reader);
}
-Stan
You could read the file in as text, remove the offending namespace text, then deserialize it.
You may need to write the "good" text back into a [memory/string/etc] stream so that the XmlSerializer's Deserialize can be called.
You need to implement IXmlSerializable to improve your custom serialization.
精彩评论