C# XML Serialization removing wrapper element - c#

I wrote because I have problem with XmlSerializer. I want XML in the following format:
<?xml version="1.0" encoding="utf-8"?>
<RootXML>
<e-Invoice>
<Version>1.03</Version>
</e-Invoice>
<TradeInvoice>
<Id>1</Id>
<Value>100</Value>
</TradeInvoice>
<e-Invoice>
<Version>1.03</Version>
</e-Invoice>
<TradeInvoice>
<Id>2</Id>
<Value>200</Value>
</TradeInvoice>
<e-Invoice>
<Version>1.03</Version>
</e-Invoice>
<TradeInvoice>
<Id>3</Id>
<Value>300</Value>
</TradeInvoice>
</RootXML>
So I created the following classes.
[XmlRoot("RootXML")]
public class Root
{
public Root()
{
RootBodies = new List<RootBody>();
}
[XmlElement("e-Invoice")]
public List<RootBody> RootBodies { get; set; }
}
public class RootBody
{
public RootBody()
{
TradeInvoice = new TradeInvoice();
EInvoiceInfo = new Version(); ;
}
[XmlElement("e-Invoice")]
public Version EInvoiceInfo { get; set; }
[XmlElement("TradeInvoice")]
public TradeInvoice TradeInvoice { get; set; }
}
public class Version
{
[XmlElement("Version")]
public string Version { get; set; }
}
public class TradeInvoice
{
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Value")]
public int Value { get; set; }
}
I have problem with removing wraper Elements (remove RootBody). I read similar topic like this link. But it does not solve my problem.

Before the actual explanation, let me point out a couple of very important things:
This XML is not very well designed and will cause a lot of other problems along the line (I'm guessing it is actually a lot more complicated than this).
The naming convention is inconsistent (e-Invoice and TradeInvoce)
The version number looks out of place, make sure this is really what they (whoever told you to do this) want before investing additional time.
This XML defines no namespaces (and probably doesn't have an XSD or DTD either)
Take a look at Google's XML design guidelines. You'll realize there is a lot of room for improvement.
There are a lot of different ways to do this, this is just one of them. I recommend you instead take it up with whoever is responsible for this design and try to change their mind.
Since you want to serialize e-Invoice and TradeInvoce without a wrapper element but still keep the order of elements (because they belong together) you need to make sure they have a common base class so they can be serialized from the same collection.
This abstract Invoice class simply tells the serializer which classes should be included during serialization via the XmlInclude attribute.
[XmlInclude(typeof(EInvoice))]
[XmlInclude(typeof(TradeInvoice))]
public abstract class Invoice
{
}
Your actual classes will be mostly unchanged
[XmlRoot("e-Invoice")]
public class EInvoice : Invoice
{
[XmlElement("Version")]
public string Version { get; set; }
}
[XmlRoot("TradeInvoice")]
public class TradeInvoice : Invoice
{
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Value")]
public int Value { get; set; }
}
Your data class doesn't need any XML-related Attributes anymore because as it is now, it can't be serialized into that format directly.
public class InvoicePair
{
public InvoicePair(EInvoice eInvoice, TradeInvoice tradeInvoice)
{
TradeInvoice = tradeInvoice;
EInvoiceInfo = eInvoice;
}
public EInvoice EInvoiceInfo { get; set; }
public TradeInvoice TradeInvoice { get; set; }
}
Your Root class has to be simplified a bit. Since we want EInvoce and TradeInvoice Elements on the same level but mixed together, we need to put them in the same collection. The XmlElement attributes will tell the serializer how to handle elements of different types without relying on the xsi:type attribute.
[XmlRoot("RootXML")]
public class Root
{
public Root()
{
RootBodies = new List<Invoice>();
}
[XmlElement(Type = typeof(EInvoice), ElementName = "e-Invoice")]
[XmlElement(Type = typeof(TradeInvoice), ElementName = "TradeInvoice")]
public List<Invoice> RootBodies { get; set; }
}
Serialization is quite straight-forward at this point. Simply add all elements to the collection of elements one after another:
public static void Serialize(IEnumerable<InvoicePair> invoices, Stream stream)
{
Root root = new Root();
foreach (var invoice in invoices)
{
root.RootBodies.Add(invoice.EInvoiceInfo);
root.RootBodies.Add(invoice.TradeInvoice);
}
XmlSerializer serializer = new XmlSerializer(typeof(Root));
serializer.Serialize(stream, root);
}
Deserialization isn't very hard either, but very prone to erros and makes a lot of assumptions:
public static IEnumerable<InvoicePair> Deserialize(Stream stream)
{
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = serializer.Deserialize(stream) as Root;
for (int i = 0; i < root.RootBodies.Count; i += 2)
{
yield return new InvoicePair(
(EInvoice) root.RootBodies[i],
(TradeInvoice) root.RootBodies[i+1]
);
}
}
Here is a working Demo Fiddle, that outputs the XML

Related

C# deserialize xml error ReadElementContentAs() methods cannot be called on an element that has child elements

I've looked at several similar SO questions but still can't solve my issue.
Using the following code I try to deserialize:
private static QCOrderInfo GetOrderFromXml(XDocument xmlDoc){
XmlSerializer serializer = new XmlSerializer(typeof(QCOrderInfo));
var post =
(QCOrderInfo)serializer.Deserialize(xmlDoc.Root.CreateReader());
return post ?? new QCOrderInfo();
}
The error resulting:
System.Xml.XmlException: ReadElementContentAs() methods cannot be called on an element that has child elements.
at System.Xml.XmlReader.SetupReadElementContentAsXxx(String methodName)
Too much code to post but view working dotnetfiddle:
https://dotnetfiddle.net/pRCWQh
Your appoved appraisers are not getting deserialized properly.
So add a class
[Serializable]
public class ApprovedAppraisers
{
[XmlElement(ElementName = "Appraiser")]
public string Appraiser { get; set; }
}
And change
public string[] ApprovedAppraisers { get; set; }
to
public ApprovedAppraisers[] ApprovedAppraisers { get; set; }

Deserialize into object from not nested xml

Imaging an XML-file like this:
<?xml version="1.0" encoding="UTF-16"?>
<treffer>
<prod_internid>123456789</prod_internid>
<md_nr>123123</md_nr>
<md_mart_id>4</md_mart_id>
<md_mtyp_nr>9876</md_mtyp_nr>
<mra_th>
<ie_th_pth>-1</ie_th_pth>
<ie_th_ea_bez>Fehler: Keine Angabe</ie_th_ea_bez>
</mra_th>
</treffer>
As you can see, there are three tags with <md_XY></md_XY>.
I want to deserialize them into an object that looks like this:
public class DeMedienXmlDto
{
[XmlElement("md_nr")]
public int MedienNr { get; set; }
[XmlElement("md_mart_id")]
public int MedienArtId { get; set; }
[XmlElement("md_mtyp_nr")]
public string MedienTypId { get; set; }
}
But this should be a property of the whole deserialized object:
[XmlRoot("treffer")]
public class DeAnalyseArtikelXmlDto
{
[XmlElement("prod_internid")]
public long ArtikelId { get; set; }
[XmlElement("treffer")]
public DeMedienXmlDto Medium { get; set; }
[XmlElement("mra_th")]
public List<DeThemenXmlDto> Themen { get; set; }
}
I've tried annotating the Medium property with [XmlElement("treffer")] since the tags are childs of <treffer> but that didn't work...
Deserializing the <mra_th>...</mra_th> works since I can annotate the list with the grouped tag but I don't have such a tag for <md...>.
How can I achieve this?
My xml deserializer looks like this:
public class XmlDeserializer : IXmlDeserializer
{
public T Deserialize<T>(string xmlFilename)
{
var returnObject = default(T);
if (string.IsNullOrEmpty(xmlFilename)) return default(T);
try
{
var xmlStream = new StreamReader(xmlFilename);
var serializer = new XmlSerializer(typeof(T));
returnObject = (T)serializer.Deserialize(xmlStream);
}
catch (Exception exception) {
LogHelper.LogError($"Das XML-File {xmlFilename} konnte nicht deserialisiert werden: {exception.Message}");
throw;
}
return returnObject;
}
}
Thanks in advance
Edit (to clarify):
I want the following tags deserialized into an object of type DeMedienXmlDto:
<md_nr>
<md_mart_id>
<md_mtyp_nr>
This is not how XmlSerializer works. The class structure must correspond to structure of the XML in order to work automatically.
This:
[XmlElement("treffer")]
public DeMedienXmlDto Medium { get; set; }
doesn't work, because there is no nested <treffer> element. The XmlElementAttribute cannot denote the parent (surrounding) element.
The are two options how to solve your situation:
Use a separate set of classes for deserialization, and a separate set representing your DTO objects. You'd then need to create a mapping.
Implement IXmlSerializable on DeAnalyseArtikelXmlDto and parse the inner XML yourself.

Omitting elementwrappers and getting the xml root value

<root xmlns:test="url" test:attr1="10" test:attr2="someValue>
<elementwrapper>
<secondElementwrapper>
<element>someValue</element>
<differentelement>anotherValue</differentelement>
</secondElementwrapper>
</elementwrapper>
</root>
The following classes are made:
public class XmlEntities
{
[XmlRoot("root")]
public class Root
{
[XmlElement("elementwrapper")]
public Elementwrapper Elementwrapper{ get; set; }
}
public class Elementwrapper
{
[XmlElement("secondElementwrapper")]
public SecondElementwrapper SecondElementwrapper{ get; set; }
}
public class Values
{
[XmlElement("element")]
public string Element{ get; set; }
[XmlElement("differentelement")]
public string Differentelement{ get; set; }
}
}
And here is where i serialize and deserialize the xml:
var reader = XmlReader.Create(url);
var xmlRecord = new XmlEntities.Root();
try
{
var serializer = new XmlSerializer(typeof(XmlEntities.Root));
xmlRecord = (XmlEntities.Root)serializer.Deserialize(reader);
reader.Close();
}
catch (Exception e) { }
I want to access xmlRecord.Element instead of xmlRecord.Elementwrapper.Values.Element
How do i get the test:attr1 value?*
If i remove the class Elementwrapper and Root, xmlRecord returns null.
[XmlAttribute] inside the Root class to get attr1 value doesnt work for me.
Thank you!
EDIT:
The element was a copy/paste wrong doing. Fixed that. I also added :test infront of the attr1, forgot that.
EDIT:
Adding the following as sgk mentioned, inside the root class, allowed me to access the attribute
[XmlAttribute("attr1", Namespace = "url")]
public string attr { get; set; }
EDIT: And is there a way to map the classes differently? So i can access xmlRecord.Element directly?
EDIT: #TonyStark seems like what i want needs to be approached a different way, but then again this already works i just need to access the element trough the nodes (xmlRecord.elementwrapper.secondelementwrapper.element) for those that are wondering.
To access the attribute of root i simply use : xmlRecord.attr after i added the xmlattribute that its written above.
I want to access xmlRecord.Element instead of xmlRecord.Elementwrapper.Values.Element - Based on the XML structure you have, to Deserialize and access the <element> value and <differentelement> value - you need to go through root-->elementwrapper-->secondElementwrapper.
How do i get the attr1 value? - add [XmlAttribute("attr1")] inside class Root
Then, [XmlElement("<elementwrapper>")] should be [XmlElement("elementwrapper")] otherwise when you Deserialize you will always get null as there is no matching element.
See below
public class XmlEntities
{
[XmlRoot("root")]
public class Root
{
[XmlElement("elementwrapper")]
public Elementwrapper Elementwrapper { get; set; }
[XmlAttribute("attr1", Namespace="url")]
public string attr1;
}
public class Elementwrapper
{
[XmlElement("secondElementwrapper")]
public SecondElementwrapper SecondElementwrapper { get; set; }
}
public class SecondElementwrapper
{
[XmlElement("element")]
public string Element { get; set; }
[XmlElement("differentelement")]
public string Differentelement { get; set; }
}
}

How to serialize the base class with derived classes

.
Hello,
I have this sample code :
public class Vehicule
{
public string Name { get; set; }
public Brand Brand { get; set; }
}
public class Car : Vehicule
{
public string Matriculation { get; set; }
}
public class Brand
{
public string Name { get; set; }
}
public class Renault : Brand
{
public string Information { get; set; }
}
If I create this instance :
var car = new Car { Name = "Clio", Matriculation = "XXX-XXX", Brand = new Renault { Name = "Renault", Information = "Contact Infos" } };
When I serialize this object like that :
var serializer = new XmlSerializer(typeof(Car), new Type[] { typeof(Renault)});
serializer.Serialize(wr, car);
I obtain this :
<?xml version="1.0" encoding="utf-8"?>
<Car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Clio</Name>
<Brand xsi:type="Renault">
<Name>Renault</Name>
<Information>Contact Infos</Information>
</Brand>
<Matriculation>XXX-XXX</Matriculation>
</Car>
But, in my project, I don't have to have informations on derived classes, I would like only elements of base classes from this instance like this :
var serializer = new XmlSerializer(typeof(Vehicule));
serializer.Serialize(wr, car);
The Xml :
<?xml version="1.0" encoding="utf-8"?>
<Vehicule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Clio</Name>
<Brand>
<Name>Renault</Name>
</Brand>
</Vehicule>
Can you please, help me to obtain the good Xml (only with base type Vehicule and Brand) ?
Many thanks
You can't magically serialize a derived class as it's base because
"...Serialization checks type of instance by calling Object.getType()
method. This method always returns the exact type of object."
http://bytes.com/topic/net/answers/809946-how-force-serialize-base-type
The solution here, if you really need to only serialize the base class is to implement the IXmlSerializable interface and create your own custom serializer.
IXmlSerializable:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable(v=vs.110).aspx
One more thought. If you can work around the limitation of outputting the extra XML elements, you are able to serialize the derived class using only the base object by either 1) using XmlIncludeAttributes on the base class to tell it which types to expect or 2) using the XmlSerializer constructor overload that takes a list of types.
Edit:
After thinking about this a little more, a workaround would be that you would add a Clone() method onto your base object, then serialize the clone of the base.
LinqPad code:
public class Vehicule
{
public string Name { get; set; }
public Brand Brand { get; set; }
public Vehicule Clone()
{
return new Vehicule { Name = this.Name, Brand = new Brand { Name = this.Brand.Name } };
}
}
public class Car : Vehicule
{
public string Matriculation { get; set; }
}
public class Brand
{
public string Name { get; set; }
}
public class Renault : Brand
{
public string Information { get; set; }
}
void Main()
{
var car = new Car { Name = "Clio", Matriculation = "XXX-XXX", Brand = new Renault { Name = "Renault", Information = "Contact Infos" } };
var vehicle = car as Vehicule;
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Vehicule));
XmlWriterSettings settings = new XmlWriterSettings
{
Encoding = new UnicodeEncoding(false, false),
Indent = false,
OmitXmlDeclaration = false
};
using(StringWriter textWriter = new StringWriter())
using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
serializer.Serialize(xmlWriter, vehicle.Clone());
textWriter.ToString().Dump();
}
}
This is one of the issues with inheritance, and another reason to favor composition imho.
I ran into the same issue on a mobile app where I had a Contact class that derives from ContactSummary. The repository returns Contact instances, but in lots of cases I only wanted the ContactSummary going over the wire to save on message sizes and data usage etc. The default Xml and Json serialisers would only work when the derived class was attributed with the [KnownType()] of the base class, but this still meant all those extra properties going over the wire.
Using inheritance it is problematic to achieve a viable solution, and I didn't want to resort to custom serialisers, and if the solution is to pollute the DTO with copy constructors and clone properties, then why not change the DTO to use composition instead?
If you have control over your DTOs, then restructuring them to use composition rather than inheritance may be the answer. In my example it was fairly simple...
public class ContactSummary
{
public string Name { get; set;}
public string Phone { get; set; }
}
public class Contact
{
public ContactSummary Summary { get; set; }
// ... other properties here
}
In your example, Car would need to contain a reference to Vehicle not inherit from it - something like...
[KnowTypes(typeof(Renault))]
public class Vehicle
{
public string Name { get; set; }
public Brand Brand { get; set; }
}
public class Car
{
public Vehicle Vehicle { get; set; }
public string Matriculation { get; set; }
}
Then when you want the 'base' type in your example, simply serialise Car.Vehicle.
I had the same problem and I got around it by re-mapping the inheriting class into the base class using AutoMapper:
MapperConfiguration config = new MapperConfiguration(cfg => cfg.CreateMap<Inheriting, Base>());
IMapper mapper = config.CreateMapper();
var baseObj = mapper.Map<Base>(InheritingObj);
There is not much you can customize on XmlSerializer out-of-the-box options.

C# deserializing nested elements with only one item

I use C# to deserialize a XML file. My XML file has the format:
<Produced_by >
<Producing_Unit>
<Unit ID="" Name=""/>
</Producing_Unit>
</Produced_by>
When deserializing I want to remove the middleman Producing_Unit. Since Produced_by will always contain only one subelement Producing_Unit specifying the Unit.
My initial thoughts on how to implement doesn't work:
public class Unit
{
public string Name { get; set; }
public string ID { get; set; }
}
public class Produced_by
{
[XmlElement("Producing_Unit")]
[XmlElement("Unit")]
public Unit Unit { get; set; }
}
It could be soleved by using [XmlArray("Producing_Unit"), XmlArrayItem("Unit")]
and then having Produced_by contain: public List<Unit> {get;set;}. But that's not what I want.
As far as I know, it's not possible to use the XML with the "Producing_Unit" tag but ditch the matching Producing_Unit class with the standard attributes short of implementing the IXmlSerializable interface. Your best bet would be to separate your application/business logic from your serialization layer.
Keep your serialization data model simple and matching your XML schema (this means including the wrapping Producing_Unit class), then simply convert to/from that data model and a cleaner data model (without Producing_Unit) for the rest of your application to work with.
EDIT: Here's an implementation using the IXmlSerializable interface. I just whipped it off and honestly, don't know if it will work for all cases.
public class Unit
{
public string Name { get; set; }
public string ID { get; set; }
}
public class Produced_by : IXmlSerializable
{
public Unit Unit { get; set; }
public void WriteXml (XmlWriter writer)
{
writer.WriteStartElement("Produced_by");
writer.WriteStartElement("Producing_Unit");
writer.WriteStartElement("Unit");
writer.WriteAttributeString("ID", this.Unit.ID);
writer.WriteAttributeString("Name", this.Unit.Name);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
}
public void ReadXml (XmlReader reader)
{
while (reader.Read())
{
if (reader.Name == "Unit")
{
this.Unit = new Unit()
{
Name = reader.GetAttribute("Name"),
ID = reader.GetAttribute("ID")
};
break;
}
}
}
public XmlSchema GetSchema()
{
return(null);
}
}
I suspect that I'm performing the reading in a poor fashion, but this works in my local test. I still recommend separating your application and serialization concerns though and avoiding having to write an implementation like this.
You could try this:
public class Unit
{
public string Name { get; set; }
public string ID { get; set; }
}
public class Producing_Unit
{
public Unit Unit { get; set; }
}
public class Produced_by
{
private Producing_Unit producing_unit;
public Producing_Unit Producing_Unit //This can't be auto-implemented since can write to properties of properties.
{
get { return producing_Unit; }
set { producing_Unit = value; }
}
[XmlIgnoreAttribute]
public Unit Unit
{
get { return producing_Unit.Unit; }
set { producing_Unit.Unit = value; }
}
}
Yes, it doesn't get rid of the 'middle-men,' but you can ignore them.

Categories