Multiple namespaces with XmlSerializer - c#

This is the scenario:
I have nested classes and need to serialize then in an xml document
[XmlRoot(Namespace="http://www.foo.bar/myschema")]
public class root
{
[XmlAttribute]
public string version { get; set; }
[XmlElement]
public child child { get; set; }
...
}
[XmlRoot(Namespace="http://www.foo.bar/myschema")]
public class child
{
[XmlElement]
public int elemA { get; set; }
[XmlElement]
public string elemB { get; set; }
...
}
I have created an method based in another example to remove additional namespaces and set a custom one:
public static void Save<T>(this T type, string path)
{
System.Xml.Serialization.XmlSerializer xs =
new System.Xml.Serialization.XmlSerializer(type.GetType());
System.Xml.Serialization.XmlSerializerNamespaces ns =
new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add("", "http://www.foo.bar/myschema");
using(XmlWriter file = XmlWriter.Create(path))
{
xs.Serialize(file, type, ns);
}
}
And I get this code as result:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://www.foo.bar/myschema" version="2.00">
<child>
<elemA>1</elemA>
...
</child>
</root>
But expected this:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://www.foo.bar/myschema" version="2.00">
<child xmlns="http://www.foo.bar/myschema">
<elemA>1</elemA>
...
</child>
</root>
We must set the custom namespace declaration in both tags. Is this possible?
Edit:
Here is a real-world example:
<?xml version="1.0" encoding="UTF-8"?>
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.01">
<idLote>200602220000001</idLote>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31060243816719000108550000000010001234567890" versao="1.01">
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
...
</NFe>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31060243816719000108550000000010011234567900" versao="1.01">
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
...
</NFe>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31060243816719000108550000000010021234567916" versao="1.01">
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
...
</NFe>
</enviNFe>

From an XML point of view your examples are identical, so the first one is perfectly fine. If you have to use the second one, there is a serious problem with our XML understanding or your processing pipeline.

Related

Adding a new field to a class to be serialized

To be able to serialize and deserialize a XML I had designed like this:
<?xml version="1.0" encoding="utf-8"?>
<DbConnections xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DbConnectionInfo>
<ServerName>SQLServer2k8</ServerName>
</DbConnectionInfo>
<DbConnectionInfo>
<ServerName>SQLServer2k8R2</ServerName>
</DbConnectionInfo>
</DbConnections>
I had written two classes like this below:
public class DbConnectionInfo
{
public string ServerName { get; set; }
}
and
[Serializable]
[XmlRoot("DbConnections")]
public class DbConnections: List<DbConnectionInfo>
{
//...
}
Now I want to expand my XML form and add one more field like this but is there is a way to design my class in a way that I don' have to REPEAT it in every XML tag? like this:
<?xml version="1.0" encoding="utf-8"?>
<DbConnections xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DbConnectionInfo>
<ServerName>SQLServer2k8</ServerName>
</DbConnectionInfo>
<DbConnectionInfo>
<ServerName>SQLServer2k8R2</ServerName>
</DbConnectionInfo>
<UseWindowsAuthentication>Yes</UseWindowsAuthentication>
</DbConnections>
So I just really added that one line to previous XML:
But my question is how should I modify my classes to add this? And is it even possible or a correct design?
<UseWindowsAuthentication>Yes</UseWindowsAuthentication>
Maybe something like this
[Serializable]
[XmlRoot("DbConnections")]
public class DbConnections
{
List<DbConnectionInfo> DbConnectionInfos;
Boolean UseWindowsAuthentication;
}
Edited to add: if you do not want nested elements, decorate your class as so
public class DbConnections
{
[XmlElement("DbConnectionInfo")]
public List<DbConnectionInfo> DbConnectionInfos;
public Boolean UseWindowsAuthentication;
}
I tested this and the following xml was serialized
XmlSerializer serializer = new XmlSerializer(typeof(DbConnections));
string xml;
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, oDbConnections);
xml = textWriter.ToString();
}
<?xml version="1.0" encoding="utf-16"?>
<DbConnections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DbConnectionInfo>
<ServerName>test</ServerName>
</DbConnectionInfo>
<DbConnectionInfo>
<ServerName>test 2</ServerName>
</DbConnectionInfo>
<UseWindowsAuthentication>true</UseWindowsAuthentication>
</DbConnections>
Here is a link to more info on decorating for xml serialization

serializing a List<knowntype> to a specific xml format

I have this list property in a class and I need to decorate it with the correct xml attributes to get it in the form:
<Attachments>
<Documents>
<Document>
the DocumentType nodes...
</Document>
<Document>
the DocumentType nodes...
</Document>
</Documents>
</Attachments>
when I serialize the object. Here is the declaration of the list property within the class:
[XmlArrayItem("Documents", IsNullable = false)]
[XmlArrayItem("Document", IsNullable = false, NestingLevel = 1)]
public List<DocumentType> Attachments
{
get
{
return this._attachments;
}
set
{
this._attachments = value;
}
}
Currently what I get is this:
<Attachments>
<Documents>
the DocumentType nodes...
</Documents>
<Documents>
the DocumentType nodes...
</Documents>
</Attachments>
Its clear to me that the "Documents" node I want should be declared something other than an XmlArrayItemAttribute. The name of the list can not change. Help me Obi Wan Kenobi, you're my only hope.
You did not specify how the 'DocumentType' looks like but can define it as follow:
public class DocumentType
{
[XmlElement("Document")]
public string Name {get; set;}
}
public class Test
{
private List<DocumentType> _attachments = new List<DocumentType>();
[XmlArrayItem("Documents", IsNullable = false)]
public List<DocumentType> Attachments
{
get
{
return this._attachments;
}
set
{
this._attachments = value;
}
}
}
The Xml after serialization:
<?xml version="1.0"?>
<Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Attachments>
<Documents>
<Document>A Node</Document>
<Document>B Node</Document>
</Documents>
</Attachments>
</Test

How can I use a class as a data model in querying XDocument?

I have an Xml document:
<?xml version="1.0" encoding="utf-8"?>
<Family xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Person member="father" id="0">
<Surname>Smith</Surname>
<Forename>Robert</Forename>
<Person member="son" id="1">
<Surname>Smith</Surname>
<Forename>Sam</Forename>
<Person member="son" id="2">
<Surname>Smith</Surname>
<Forename>Jeff</Forename>
</Person>
</Person>
<Person member="daughter" id="3">
<Surname>Smith</Surname>
<Forename>Sarah</Forename>
</Person>
</Person>
</Family>
...and a few supporting classes as follows:
[XmlRoot]
public class Family {
[XmlElement]
public List<Person> Person;
}
public class Person {
[XmlAttribute("member")]
public MemberType Member { get; set; }
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement]
public string Surname { get; set; }
[XmlElement]
public string Forename { get; set; }
[XmlElement("Person")]
public List<Person> People;
}
public enum MemberType {
Father,
Mother,
Son,
Daughter
}
Now, say Family has a method defined as such:
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
// also, I know that this SelectMany will not work - assume this foreach works
// on a flattened list of people
foreach (var p in family.Person.SelectMany()) {
if(predicate(p)) {
yield return p;
}
}
}
...but I don't want to deserialize the Xml to the Family and Person classes. I would like to simply load the XDocument and query that directly - but working with XElement, XAttribute, and XName is not that friendly when providing an API. I realize that I need the classes - Family & Person - but they are simply models.
Can I have a Find method where I can pass something like:
IEnumerable<Person> people = someBusinessObj.Find(p => p.Forename == "Jeff");
Update
I would prefer a solution that does not involve an open-source project (as #MartinHonnen refers).
Why do you not want to deserialize the XML into objects? This will give you exactly the programmatic interface you require. I would:
Deserialize using the XmlSerializer class.
Allow you users to query via Linq-to-Objects.
All this requires very little effort to implement!

Deserialise XML File to .Net Object

I'm trying to deserialize an xml file to a .NET object by doing something like:
CarCollection myCarCollection = null;
string path = "CarCollection.xml";
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
StreamReader reader = new StreamReader(path);
myCarCollection= (CarCollection)serializer.Deserialize(reader);
reader.Close();
Here is the xml file I'm using:
<?xml version="1.0" encoding="utf-8" ?>
<CarCollection>
<Car ID="A">
<CarType Make="Ford" Model="Focus" />
<CarOwner Name="Tom">
<Report Type="Service">
<ReportList>
<Date>20-08-2010</Date>
</ReportList>
</Report>
</CarOwner>
</Car>
<Car ID="B">
<CarType Make="Vauxhall " Model="Corsa" />
<CarOwner Name="Joe">
<Report Type="Service">
<ReportList>
<Date>10-10-2008</Date>
<Date>10-10-2009</Date>
<Date>10-10-2010</Date>
</ReportList>
</Report>
<Report Type="Accident">
<ReportList>
<Date>20-01-2011</Date>
</ReportList>
</Report>
</CarOwner>
</Car>
</CarCollection>
I've tried many things but can't seem to get it working.
Could anyone please help me how to do deserialize to a .NET object.
Here is the C# Objects
[Serializable()]
[XmlRoot("CarCollection")]
public class CarCollection
{
[XmlArray("Car")]
[XmlArrayItem("Car", typeof(Car))]
public Car[] Cars { get; set; }
}
[Serializable()]
public class Car
{
[XmlAttribute("Make")]
public string CarMakeType { get; set; }
[XmlAttribute("Model")]
public string CarModelType { get; set; }
[XmlArray("CarOwner")]
[XmlArrayItem("CarOwner", typeof(CarOwner))]
public CarOwner[] CarOwners { get; set; }
}
[Serializable()]
public class CarOwner
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlArray("Report")]
[XmlArrayItem("Report", typeof(Report))]
public Report[] Reports { get; set; }
}
[Serializable()]
public class Report
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlArray("Report")]
[XmlArrayItem("Report", typeof(DateTime))]
public DateTime[] Reports { get; set; }
}
Tangentially you might find some benefit in using XSD to generate XML from your classes.
I bet this is due to the date format.
the xmlns declaration is also missing.
The Felice suggestion is a good one. Try to produce the desired result with serializing, before trying to deserialize
Read this page in the MSDN to verify your code.
The yellow note halfway down the page specifies what requirements must be met by collections.
Also: pass the Car type too, to the Serializer constructor.
EDIT
The Report and Car tags are not closed!
EDIT
Here is the output when serializing. Spot the differences there are many. The biggest problem is how you are serializing the arrays. Start using plurals (Cars, Owners) for collections that wil make it more readable.
<?xml version="1.0" encoding="utf-8"?>
<CarCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Car>
<Car Make="make">
<CarOwner>
<CarOwner Name="name1">
<Report>
<Report Type="rtype">
<Report>
<Report>2011-01-25T15:22:52.703125+01:00</Report>
</Report>
</Report>
</Report>
</CarOwner>
</CarOwner>
</Car>
<Car Make="make2">
<CarOwner>
<CarOwner Name="name3">
<Report>
<Report Type="rtype">
<Report>
<Report>2011-01-25T15:22:52.703125+01:00</Report>
</Report>
</Report>
</Report>
</CarOwner>
</CarOwner>
</Car>
</Car>

How can I Serialize Properly

If I have a class MovieClass as
[XmlRoot("MovieClass")]
public class Movie
{
[XmlElement("Novie")]
public string Title;
[XmlElement("Rating")]
public int rating;
}
How can I've an attribute "x:uid" in my "Movie" element, so that the output when XmlSerializer XmlSerializer s = new XmlSerializer(typeof(MovieClass)) was used
is like this:
<?xml version="1.0" encoding="utf-16"?>
<MovieClass>
<Movie x:uid="123">Armagedon</Movie>
</MovieClass>
and not like this
<?xml version="1.0" encoding="utf-16"?>
<MovieClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie x:uid="123" Title="Armagedon"/>
</MovieClass>
Note: I want the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" removed, if possible.
I answered this in your original post, but I think this one is worded better so I will post it here as well, if it gets closed as duplicate you can modify your original post to mirror this question.
I don't think this is possible without having Title be a custom type or explicitly implementing serialization methods.
You could do a custom class like so..
class MovieTitle
{
[XmlText]
public string Title { get; set; }
[XmlAttribute(Namespace="http://www.myxmlnamespace.com")]
public string uid { get; set; }
public override ToString() { return Title; }
}
[XmlRoot("MovieClass")]
public class Movie
{
[XmlElement("Movie")]
public MovieTitle Title;
}
which will produce:
<MovieClass xmlns:x="http://www.myxmlnamespace.com">
<Movie x:uid="movie_001">Armagedon</Movie>
</MovieClass>
Although the serializer will compensate for unknown namespaces with a result you probably won't expect.
You can avoid the wierd behavior by declaring your namespaces and providing the object to the serializer..
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("x", "http://www.myxmlnamespace.com");
It's not valid XML if you don't have x declared as a namespace prefix. Quintin's response tells you how to get valid XML.

Categories

Resources