Omitting elementwrappers and getting the xml root value - c#

<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; }
}
}

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; }

C# XML Serialization removing wrapper element

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

What Model of classes should I use to generate this Xml structure? (C#)

I need to create a model that will serialize to an Xml of this form (this is part of a bigger Xml):
<ManufacturerPartNumbers>
<ManufacturerPartNumber>26-12345-8W</ManufacturerPartNumber>
<ManufacturerPartNumber>26-12345-8Y</ManufacturerPartNumber>
<ManufacturerPartNumber>26-12345-8Z</ManufacturerPartNumber>
</ManufacturerPartNumbers>
I've tried using
[XmlType(TypeName = "ManufacturerPartNumber")]
public class ManufacturerPartNumberModel
{
public string Number { get; set; }
}
And in the upper class:
public List<ManufacturerPartNumberModel> ManufacturerPartNumbers { get; set; }
But this generates an extra Xml Node:
<ManufacturerPartNumbers>
<ManufacturerPartNumber>
<Number>26-12345-8W</Number>
</ManufacturerPartNumber>
</ManufacturerPartNumbers>
And 5 minutes later I found the answer :)
I just had to add [XmlTextAttribute] to the Number property and it will serialize it as a nameless inline attribute inside ManufacturerPartNumberModel
[XmlType(TypeName = "ManufacturerPartNumber")]
public class ManufacturerPartNumberModel
{
[XmlTextAttribute]
public string Number { get; set; }
}

XML Deserialization returns empty array

yet another XML Deserialization question.
I have checked several other threads and tried most of the solutions there, but to no avail.
The XML I receive can't be modded (or at least not easily) here it is:
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<ActueleVertrekTijden>
<VertrekkendeTrein>
<RitNummer>37047</RitNummer>
<VertrekTijd>2012-11-13T15:40:00+0100</VertrekTijd>
<EindBestemming>Sneek</EindBestemming>
<TreinSoort>Stoptrein</TreinSoort>
<Vervoerder>Arriva</Vervoerder>
<VertrekSpoor wijziging=\"false\">3</VertrekSpoor>
</VertrekkendeTrein>
<VertrekkendeTrein>
<RitNummer>10558</RitNummer>
<VertrekTijd>2012-11-13T15:46:00+0100</VertrekTijd>
<EindBestemming>Rotterdam Centraal</EindBestemming>
<TreinSoort>Intercity</TreinSoort>
<RouteTekst>Heerenveen, Steenwijk, Utrecht C</RouteTekst>
<Vervoerder>NS</Vervoerder>
<VertrekSpoor wijziging=\"false\">4</VertrekSpoor>
</VertrekkendeTrein>
<VertrekkendeTrein>
<RitNummer>37349</RitNummer>
<VertrekTijd>2012-11-13T15:59:00+0100</VertrekTijd>
<EindBestemming>Groningen</EindBestemming>
<TreinSoort>Sneltrein</TreinSoort>
<RouteTekst>Buitenpost</RouteTekst>
<Vervoerder>Arriva</Vervoerder>
<VertrekSpoor wijziging=\"false\">5b</VertrekSpoor>
</VertrekkendeTrein>
</ActueleVertrekTijden>
There are more elements (always a minumum of 10)
Now these are the classes I am deserializing too:
[Serializable, XmlRoot(ElementName="ActueleVertrekTijden", DataType="VertrekkendeTrein", IsNullable=false)]
public class ActueleVertrekTijden
{
[XmlArray("ActueleVertrekTijden")]
public VertrekkendeTrein[] VertrekLijst { get; set; }
}
[Serializable]
public class VertrekkendeTrein
{
[XmlElement("RitNummer")]
public string RitNummer { get; set; }
[XmlElement("VertrekTijd")]
public string VertrekTijd { get; set; }
[XmlElement("EindBestemming")]
public string EindBestemming { get; set; }
[XmlElement("Vervoerder")]
public string Vervoerder { get; set; }
[XmlElement("VertrekSpoor")]
public string VertrekSpoor { get; set; }
}
I omitted the others for the time being. The XmlRoot part I added because I got a "xmlsn="-error. So had to set the XmlRoot.
Now the Deserializer:
public ActueleVertrekTijden Deserialize<ActueleVertrekTijden>(string s)
{
var ser = new XmlSerializer(typeof(ActueleVertrekTijden));
ActueleVertrekTijden list = (ActueleVertrekTijden)ser.Deserialize(new StringReader(s));
return list;
}
It does return a ActueleVertrekTijden class but the VertrekLijst array remains null
You need to omit the wrapper namespace, because your array elements are appearing directly below the container ActueleVertrekTijden class, without any collection wrapper element. i.e. change
[XmlArray("ActueleVertrekTijden")]
public VertrekkendeTrein[] VertrekLijst { get; set; }
to
[XmlElement("VertrekkendeTrein")]
public VertrekkendeTrein[] VertrekLijst { get; set; }
Reference here

Serialize error from user control

I have created a user control (CheckedDirTree) that exposes a CheckedFolder property which in turn returns an IEnumerable of the FullPath property of Nodes checked in the control. Something like this:
public IEnumerable<string> CheckedFolders
{
get
{
foreach (TreeNode node in treeView1.Nodes[0].DescendantNodes())
{
if(node.Checked && !node.FullPath.Equals(_directoryRoot))
yield return node.FullPath;
}
}
}
This is being fed to another class's (SymbolsShareDto) Folders property after this cntrol is shown in a grid and the user has checked some folders:
using (var dirControl = new CheckedDirForm(symbolsShare))
{
if (dirControl.ShowDialog() == DialogResult.OK)
{
var symbolsShareObj = bindingSourceShare.Current as SymbolShareModel;
if (symbolsShareObj != null) symbolsShareObj.Folders = dirControl.CheckedFolders;
}
}
[DataContract]
public class SymbolShareDTO
{
public SymbolShareDTO(){}
[DataMember]
public string Share { get; set; }
[DataMember]
public string BackupTo { get; set; }
[DataMember]
public IEnumerable<string> Folders { get; set; }
public override string ToString()
{
return string.Format("Share: {0}{1}BackupTo: {2}{3}Folders: {4}", Share, Environment.NewLine, BackupTo,
Environment.NewLine, Folders.Count());
}
}
However when I serialize the SymbolsShareDto, I'm getting an error saying
CheckedDirTree+<get_CheckedFolders>d__6' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Any thoughts ? Do I need to return a new List from CheckedFolders property instead.
thanks
Sunit
Do CheckedFolders.ToArray() and serialize that.
use
[DataContract] for your class (msdn)
and [DataMember] for each property (msdn)
You're probably trying to send instances of SymbolsShareDto through WCF.
All you have to do is to mark it with DataContractAttribute and all the property with DataMember :
[DataContract]
public class SymbolsShareDto
{
[DataMember]
public int Data1 { get; set;};
[DataMember]
public int Data2 { get; set;}
}
I hope this help.

Categories