Formatters for deep nested XML node serialization - c#

I need to serialize my C# class to XML looking something like so:
<request>
<session>12345</session>
<page>1</page>
<elements_per_page>999</elements_per_page>
<location>
<zone>aaaa</zone>
<region>bbbb</region>
<coordinates>
<lat>38.680632</lat>
<lon>-96.5001</lon>
</coordinates>
</location>
</request>
What I don't want is 3 classes (request, location, coordinates), I just want 1 class with all changable attributes as root of that class and then added some serialization tags that would create that nested XML, is that at all possible?
Let's start with the bare class:
[XmlRoot]
class request
{
[XmlElement]
public int session { get; set; }
[XmlElement]
public int page { get; set; }
[XmlElement]
public int elements_per_page { get; set; }
[?]
public string zone { get; set; }
[?]
public string region { get; set; }
[?]
public decimal lat { get; set; }
[?]
public decimal lon { get; set; }
}
How do I map them so the XML like I described is created? Thanks for your help good people :)

"is that at all possible?"
No. What's the problem of having 3 classes? How would you want to make a XML-to-static-code linking at all in some other way?
Moreover, if you just want to dirtly spit out some XML, you could use, for example, System.Xml.XmlDocument and build an xml from scratch which you could serialize with System.Xml.Serialization.XmlSerializer. Like this:
public string SerializeRequest(Request request)
{
XmlDocument document = new XmlDocument();
XmlElement requestElement = document.CreateElement("request");
XmlElement sessionElement = document.CreateElement("session");
sessionElement.InnerText = request.session.ToString(CultureInfo.InvariantCulture);
XmlElement pageElement = document.CreateElement("page");
pageElement.InnerText = request.page.ToString(CultureInfo.InvariantCulture);
XmlElement elementsPerPageElement = document.CreateElement("elements_per_page");
elementsPerPageElement.InnerText = request.elements_per_page.ToString(CultureInfo.InvariantCulture);
XmlElement zoneElement = document.CreateElement("zone");
zoneElement.InnerText = request.zone;
XmlElement regionElement = document.CreateElement("region");
regionElement.InnerText = request.region;
XmlElement latElement = document.CreateElement("lat");
latElement.InnerText = request.lat.ToString(CultureInfo.InvariantCulture);
XmlElement lonElement = document.CreateElement("lon");
lonElement.InnerText = request.lon.ToString(CultureInfo.InvariantCulture);
XmlElement coordinatesElement = document.CreateElement("coordinate");
coordinatesElement.AppendChild(latElement);
coordinatesElement.AppendChild(lonElement);
XmlElement locationElement = document.CreateElement("location");
locationElement.AppendChild(zoneElement);
locationElement.AppendChild(regionElement);
locationElement.AppendChild(coordinatesElement);
requestElement.AppendChild(sessionElement);
requestElement.AppendChild(pageElement);
requestElement.AppendChild(elementsPerPageElement);
requestElement.AppendChild(locationElement);
document.AppendChild(requestElement);
string serializedObj = document.OuterXml;
return serializedObj;
}

Related

Desearilizing Xml with multiple InnerText Nodes

I am having trouble with deserializing a XML into C# Model. I am pulling that XML from external API, so i have no control over it. It has multiple "InnerText" nodes, that serialized cant deserialize correctly(last one wins, orhers are lost).
I`ve already fixed this using XmlDocument Class, but i need to do it with model instead.
The XML i am trying to deserialize:
<root>
<passage>
<hlword>Test</hlword>
your Internet.....
<hlword>test</hlword>
from Ookla.
</passage>
</root>
C# Classes:
[XmlRoot(ElementName = "passage")]
public class Passage
{
[XmlElement(ElementName = "hlword")]
public List<string> Hlword { get; set; }
[XmlText]
public string InnerText { get; set; }
}
[XmlRoot(ElementName = "root")]
public class Root
{
[XmlElement(ElementName = "passage")]
public List<Passage> Passage { get; set; }
[XmlText]
public string InnerText { get; set; }
}
From example above, i need to extract: "Test your internet..... test from Ookla", Instead I am getting List of Passage class with 2 Hlwords (inner text "test") and inner text on Root class "from Ookla". All the text after elements is ommited.
Try reading the innertext using XmlDocument
var doc = new XmlDocument();
doc.LoadXml(xmlString);
var text = doc.SelectSingleNode("root/passage").InnerText;

Deserialize only specific Nodes from Xml to Object

I am trying to take specific Nodes from an Xml and write it into a class. I have this.
public class TradeMark
{
[XmlElement]
public string MarkVerbalElementText { get; set; }
[XmlElement]
public int MarkCurrentStatusCode { get; set; }
[XmlElement]
public string ExpiryDate { get; set; } = "";
}
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(TradeMark));
using (TextReader reader = new StreamReader(pathToImportFile))
{
tradeMark = (TradeMark)serializer.Deserialize(reader);
}
}
In my Xml Data, there are more Node than just these 3. Now when i run the Code it says ...... was not expected. I guess bc. It tries to deserialize everything than only these 3 Infomartionen in Class TradeMark.
Can anyone help?
XML
<?xml version="1.0" encoding="UTF-8"?>
<Transaction xmlns="http://euipo.europa.eu/trademark/data" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://euipo.europa.eu/trademark/data http://euipo.europa.eu/schemas/trademark/EM-TM-TradeMark-V3-2.xsd">
<TransactionHeader>
<SenderDetails>
<RequestProducerDateTime>2018-08-18T15:33:35</RequestProducerDateTime>
</SenderDetails>
</TransactionHeader>
<TradeMarkTransactionBody>
<TransactionContentDetails>
<TransactionIdentifier>017690538</TransactionIdentifier>
<TransactionCode>EM-Trade Mark</TransactionCode>
<TransactionData>
<TradeMarkDetails>
<TradeMark operationCode="Insert">
<RegistrationOfficeCode>EM</RegistrationOfficeCode>
<ApplicationNumber>017690538</ApplicationNumber>
<ApplicationDate>2018-01-16</ApplicationDate>
<RegistrationDate>2018-06-14</RegistrationDate>
<ApplicationLanguageCode>en</ApplicationLanguageCode>
<SecondLanguageCode>es</SecondLanguageCode>
<ExpiryDate>2028-01-16</ExpiryDate>
<MarkCurrentStatusCode milestone="23" status="1">Registered</MarkCurrentStatusCode>
<MarkCurrentStatusDate>2018-06-15</MarkCurrentStatusDate>
<KindMark>Individual</KindMark>
<MarkFeature>Figurative</MarkFeature>
<TradeDistinctivenessIndicator>false</TradeDistinctivenessIndicator>
<WordMarkSpecification>
<MarkVerbalElementText>Tiens</MarkVerbalElementText>
</WordMarkSpecification>
Most likely this happens because your XML has a default namespace and Transaction is within this namespace.
You need to mark your class with XmlRootAttribute like so:
[XmlRootAttribute("TradeMark", Namespace="http://euipo.europa.eu/trademark/data",
IsNullable = false)]
public class TradeMark
XmlIgnore is what you're looking for.
MSDN Docs
See clarification in this answer, as the docs only state XmlIgnore will be ignored on serialize, but will also be ignored when deserializing.
From your example:
public class TradeMark
{
[XmlElement]
public string MarkVerbalElementText { get; set; }
[XmlElement]
public int MarkCurrentStatusCode { get; set; }
[XmlElement]
public string ExpiryDate { get; set; } = "";
[XmlIgnore]
public string IgnoreMe { get; set; } // This will be ignored
}

c# parse string to xml using StringReader

I have an XML and I load it in an class.
This is my XML
<out_policySystem xmlns:msl="http://www.ibm.com/xmlmap" xmlns:io="" xmlns:xs4xs="http://www.w3.org/2001/XMLSchema">
<BGBAResultadoOperacion>
<Severidad>OK</Severidad>
</BGBAResultadoOperacion>
<permiteOperar>true</permiteOperar>
<Lista xmlns:ns0=\"http://CalypsoPolicySystem_lib/service\">
<Codigo>ODM-006</Codigo>
<Descripcion>Aviso</Descripcion>
<DescripcionTecnica>XXXX</DescripcionTecnica>
</Lista>
</out_policySystem>
I have define my classes like this.
[XmlRoot(ElementName = "out_policySystem")]
public partial class output_policySystem
{
public BGBAResultadoOperacion BGBAResultadoOperacion { get; set; }
public bool permiteOperar { get; set; }
public List[] Lista { get; set; }
}
public partial class BGBAResultadoOperacion
{
public string Severidad { get; set; }
}
public partial class List
{
public string Codigo { get; set; }
public string Descripcion { get; set; }
public string DescripcionTecnica { get; set; }
}
I read this like this.
XmlNodeList elemlist = xDoc.GetElementsByTagName("out_policySystem");
string result = elemlist[0].InnerXml;
XmlSerializer serializer = new XmlSerializer(typeof(BGBAResultadoOperacion));
using (StringReader reader = new StringReader(result))
{
result = (BGBAResultadoOperacion)(serializer.Deserialize(reader));
}
the value of result is this.
<BGBAResultadoOperacion><Severidad>OK</Severidad></BGBAResultadoOperacion><permiteOperar>true</permiteOperar><Lista><Codigo>ODM-006</Codigo><Descripcion>Aviso</Descripcion><DescripcionTecnica>xxxx</DescripcionTecnica></Lista>
What I need is to get the value of BGBAResultadoOperacion
when i set
using (StringReader reader = new StringReader(result))
{
result = (BGBAResultadoOperacion)(serializer.Deserialize(reader));
}
result get XML error...
There are multiple root elements. Line 1, position 76.
XML node out_policySystem has three root elements inside it. I need to parse only BGBAResultadoOperacion
How can I get it?
Thanks
That's because of this line:
elemlist[0].InnerXml
Which returns an XML Fragment, not an XML Document.
<BGBAResultadoOperacion>
<Severidad>OK</Severidad>
</BGBAResultadoOperacion>
<permiteOperar>true</permiteOperar>
<Lista xmlns:ns0=\"http://CalypsoPolicySystem_lib/service\">
<Codigo>ODM-006</Codigo>
<Descripcion>Aviso</Descripcion>
<DescripcionTecnica>XXXX</DescripcionTecnica>
</Lista>
So either use the .OuterXML, or use XElement.CreateReader() as described in the answer here: Serialize an object to XElement and Deserialize it in memory

Tricks for deserialize this xml element

A legacy application transmits data as xml elements.
<frame>
<reply>
<id>value_id</id>
<resultCode>value</resultCode>
<readSampleIDs>
<returnValue>
<Sample>
<SampleID> value_SampleID </SampleID>
<SamplePC> value_SamplePC </SamplePC>
<antennaName> value_antennaName </antennaName>
<channel> value_channel </channel>
<power> value_power </power>
<polarization> value_polarization </polarization>
<inventoried> value_inventoried </inventoried>
</Sample>
…
<Sample>
…
</Sample>
</returnValue>
</readSampleIDs>
</reply>
Currently the information is extracted parsing the string word by word.
I think that xml element can be deserialized into an object directly with XmlSerializer but I have some doubts on how to do it.
The element frame contains only one reply. Do I really need two different classes ?
Inside returnValue there can be zero or more Sample. In my class what is the correct type, List<Sample> or Sample[] ? Is there a real difference between the two options during deserialization ?
Most fields inside Sample are optional. How do I model this ?
When an object is serialized with XmlSerializer information about xml version and additional attributes on root element are automatically added such as:
<?xml version="1.0" encoding="utf-8"?>
<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cpandl.com">
They are not present in my example code so I'm afraid deserialization can complain about it and possibly fail.
Thanks
Filippo
If your xml contains a namespace (like in your example) then you can use these classes:
[XmlRoot(ElementName = "Sample")]
public class Sample
{
[XmlElement(ElementName = "SampleID")]
public string SampleID { get; set; }
[XmlElement(ElementName = "SamplePC")]
public string SamplePC { get; set; }
[XmlElement(ElementName = "antennaName")]
public string AntennaName { get; set; }
[XmlElement(ElementName = "channel")]
public string Channel { get; set; }
[XmlElement(ElementName = "power")]
public string Power { get; set; }
[XmlElement(ElementName = "polarization")]
public string Polarization { get; set; }
[XmlElement(ElementName = "inventoried")]
public string Inventoried { get; set; }
}
[XmlRoot(ElementName = "readSampleIDs")]
public class ReadSampleIDs
{
[XmlArray(ElementName = "returnValue")]
[XmlArrayItem(ElementName = "Sample")]
public List<Sample> Sample { get; set; }
}
[XmlRoot(ElementName = "reply", Namespace = "http://www.cpandl.com")]
public class Reply
{
[XmlElement(ElementName = "id")]
public string Id { get; set; }
[XmlElement(ElementName = "resultCode")]
public string ResultCode { get; set; }
[XmlElement(ElementName = "readSampleIDs")]
public ReadSampleIDs ReadSampleIDs { get; set; }
}
If you are only interested in node reply and this is the only one. You can deserialize only this node:
XNamespace loNameSpace = "http://www.cpandl.com";
XDocument loDoc = XDocument.Parse(Properties.Settings.Default.TransmitsData);
var loReplyElement = loDoc.Element(loNameSpace.GetName("PurchaseOrder"))
.Element(loNameSpace.GetName("frame"))
.Element(loNameSpace.GetName("reply"));
using (var loReader = loReplyElement.CreateReader())
{
var loSerializer = new XmlSerializer(typeof(Reply));
var loReply = (Reply)loSerializer.Deserialize(loReader);
Console.WriteLine(loReply.Id);
}

What is the best way to manually parse an XElement into custom objects?

I have an XElement variable named content which consists of the following XML:
<content>
<title>Contact Data</title>
<p>This is a paragraph this will be displayed in front of the first form.</p>
<form idCode="contactData" query="limit 10; category=internal"/>
<form idCode="contactDataDetail" query="limit 10; category=internal">
<title>Contact Data Detail</title>
<description>This is the detail information</description>
</form>
</content>
I now want to simply run through each of the level-1 nodes and parse them into objects. Back in C# 2.0 I use to do this with XmlReader, check the type of node, and parse it accordingly.
But what is the best way to parse the XML nodes with LINQ, I would expect something like this:
var contentItems = from contentItem in pageItem.content.DescendantNodes()
select new ContentItem
{
Type = contentItem.Element()
};
foreach (var contentItem in contentItems)
{
switch (contentItem.Type)
{
case "title":
...(parse title)...
case "p":
...(parse p)...
case "form":
...(parse form)...
}
}
where:
public class ContentItem
{
public string Type { get; set; }
public string IdCode { get; set; }
public XElement Content { get; set; }
}
Does it have to be XElement? I would (either manually, or via xsd.exe) just create classes that map to the element/attribute names - and use XmlSerializer - in particular via StringReader:
Content content;
using(StringReader sr = new StringReader(xml))
using(XmlReader xr = XmlReader.Create(sr)) {
XmlSerializer ser = new XmlSerializer(typeof(Content));
content = (Content)ser.Deserialize(xr);
}
(edit)
With entity classes:
[XmlRoot("content")]
public class Content {
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("p")]
public string Description { get; set; }
[XmlElement("form")]
public List<ContentForm> Forms { get; set; }
}
public class ContentForm {
[XmlAttribute("idCode")]
public string Id { get; set; }
[XmlAttribute("query")]
public string Query { get; set; }
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("description")]
public string Description { get; set; }
}
I'd suggest inheriting XElement, and implement properties for the stuff you want in it.
These properties shouldn't use backing fields, but rather work directly with the underlying XML element. That way, you'll keep object in sync with XML.
With XML to LINQ one pulls specific data items out of the XML, rather than iterating through the XML looking for what is found.
var results = from node in XmlDocument.SomeContext(...)
select new MyType {
Prop1 = node.Element("One").Value,
Prop2 = node.Element("One").Attributes().Where(
a => A.Value.Contains("Foo").Value
};
If conditions are needed, then (extension) methods and arbitrary expressions can be used (null-coalescing operator, ??, can be very helpful to default).

Categories

Resources