Serialization Class to XML Problem List<> - c#

I have trouble exporting my xml from class object, when I generate my XML a node nests inside one with the same name the problem will be serialized a list.
I have an object like this:
Currently my object is built alone since I am only doing tests, but the idea is to first obtain an XML that generates the structure I need.
public class PmtInf
{
public string PmtInfId = "PAYMENT REFERENCE";//Referencia de pago
public string PmtMtd = "TRF";
public PmtTpInf PmtTpInf = new PmtTpInf();
public string ReqdExctnDt = "2020-06-24";//Fecha de pago
public Dbtr Dbtr = new Dbtr();
public InitgPty DbtrAcct = new InitgPty();
//Problem this Property
public List<CdtTrfTxInf> CdtTrfTxInf = new List<CdtTrfTxInf>() { new CdtTrfTxInf(), new CdtTrfTxInf() };
}
public class CdtTrfTxInf
{
public PmtId PmtId = new PmtId();
public Amt Amt = new Amt();
public CdtrAgt CdtrAgt = new CdtrAgt();
public Dbtr Cdtr = new Dbtr();
public InitgPty CdtrAcct = new InitgPty();
}
To serialize and export my XML I use this code
I use the XmlSerializer to build the XML since it is the way I found investigated in the same way if there is any other way to generate it I am open to ideas
var XML = new System.Xml.Serialization.XmlSerializer(Objeto.GetType());
var Path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)"//XMLExport.xml";
FileStream file = File.Create(Path);
XML.Serialize(file, Objeto);
file.Close();
`
The XML I get nests the property <CdtTrfTxInf> but I need both <CdtTrfTxInf> to be outside <CdtTrfTxInf> the one that is generated more. Maybe the XML is poorly structured but this is how it is requested
<PmtInf>
<PmtInfId>PAYMENT REFERENCE</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<PmtTpInf>
</PmtTpInf>
<ReqdExctnDt>2020-06-24</ReqdExctnDt>
<Dbtr>
</Dbtr>
<DbtrAcct>
</DbtrAcct>
<!-- Here its the problem my node CdtTrfTxInf its un other CdtTrfTxInf -->
<CdtTrfTxInf>
<CdtTrfTxInf>
more....
</CdtTrfTxInf>
<CdtTrfTxInf>
more....
</CdtTrfTxInf>
</CdtTrfTxInf>
</PmtInf>
I need that my <CdtTrfTxInf> be like property N times. The serializer do in other CdtTrfTxInf, the correct its like this:
<PmtInf>
<PmtInfId>PAYMENT REFERENCE</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<PmtTpInf>
</PmtTpInf>
<ReqdExctnDt>2020-06-24</ReqdExctnDt>
<Dbtr>
</Dbtr>
<DbtrAcct>
</DbtrAcct>
<CdtTrfTxInf>
more....
</CdtTrfTxInf>
<CdtTrfTxInf>
more....
</CdtTrfTxInf>
</PmtInf>`
How can I get that structure or what modifications should I make so that my object constructs an XML like the one I need

Adding XmlElementAttribute to CdtTrfTxInf should do the trick.
public class PmtInf
{
...
[XmlElement] // Add this attribute
public List<CdtTrfTxInf> CdtTrfTxInf = ...
}

Related

Cannot load internal nodes from XML file

I have a xml schema in the form of
<?xml version="1.0" encoding="UTF-8"?>
<project ref="edward" name="Edward(A)">
<desc/>
<Zones>
<Zone ref="1" name="Zone1"/>
<Zone ref="2" name="Zone2"/>
<Zone ref="3" name="Zone3"/>
<Zone ref="4" name="Zone4"/>
</Zones>
</project>
I am trying to extract all the Zones value using Xml to Linq (not an expert)
I tried
string xmlString = System.IO.File.ReadAllText("..\\..\\..\\v1.xml");
XDocument xdoc = new XDocument();
xdoc=XDocument.Parse(xmlString);
var ele = xdoc.Descendants("Zones")
.Select(x => (string)x.Element("name"))
.FirstOrDefault();
// this is null
var result = xdoc.Element("project").Descendants("Zones").Descendants("Zone");
foreach (var item in result)
{
Console.WriteLine(item.name); //what should be here
}
You could add the items to a list or using a Dictionary<int, string> may look something like...
Dictionary<int, string> Zones = new Dictionary<int, string>();
foreach (var item in result) {
int.TryParse(item.Attribute("ref").Value, out int value);
Zones.Add(value, item.Attribute("name").Value);
Console.WriteLine(item.Attribute("name").Value);
Console.WriteLine(item.Attribute("ref").Value);
}
Another approach using two simple Classes…
Looking closer, I am betting using a couple of classes and the System.Xml.Serialization; library may make this easier. Given the XML file. One Class appears obvious… called a Zone object with the int and string properties. Then another Class we could call ZoneProject.
The ZoneProject Class would have three (3) properties. In this case two string properties called Ref and Name, and a List<Zone> of Zone objects called Zones. The bare minimum of these two classes may look something like…
The Zone Class
public class Zone {
[XmlAttribute("ref")]
public int Ref { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
}
The ZoneProject Class
[XmlRoot("project")]
public class ZoneProject {
[XmlAttribute("ref")]
public string Ref { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
[XmlArray("Zones")]
public List<Zone> Zones { get; set; }
}
To help “deserialize” the XML we added additional qualifiers to help mate the XML element/attributes to the particular property of each class. The XmlRoot is the base class ZoneProject with the XmlAttributes and the collection of Zone objects using the XmlArray qualifier. This property will be a List<Zone> collection. The same follows for the Zone Class.
With this set up, we can use the System.Xml.Serialization; library and deserialize the XML file into a single ZoneProject object. Something like…
string filePath = #"PathToYourXML_File\file.xml";
XmlSerializer serializer = new XmlSerializer(typeof(ZoneProject));
ZoneProject TheZoneProject;
using (FileStream fs = File.OpenRead(filePath)) {
TheZoneProject = (ZoneProject)serializer.Deserialize(fs);
}
It should be noted, that the answer from #Enyra on the SO question How to read XML file into List<>? …was helpful in the answer above.
I hope this makes sense.
Please check your xml file path. I think that , problem here.
string xmlString = System.IO.File.ReadAllText(dicorrectPath);
You can collect you zones attributes with Linq:
var result = xdoc.Element("project").Descendants("Zones").Descendants("Zone");
var zonesAtributes = result.Select(x =>
new
{
name = x.Attribute("name").Value,
#ref = x.Attribute("ref").Value
}).ToArray();

<xmlns='' > was not expected. But cannot define XMLroot as it changes. C#

I have a site that imports bookings from an external XML file and stores them as nodes in an Umbraco site.
The issue:
Basically, The system in which I exported the bookings from has changed how it exports the XML and the original root node.
I am assuming this is an issue related to this root node as many answers on stack have mentioned this in regards to the error in the subject title, but none that I could see have covered how to deal with a dynamic rootnode.
What I used to have in the XML file:
<Bookings>
<row id="1">
<FirstBookingAttribute>....
</row>
</Bookings>
Now is dynamic and can end up looking like:
<BookingsFebruary2017 company="CompanyNameHere">
<row id="1">
<firstBookingAttribute>....
</row>
</BookingsFebruary2017>
Because of this I am not entirely sure how to set the XMLroot node in C#.
Incase this issue is not related to the root node, I have pasted the erroring code below:
Controller File
var reader = new StreamReader(tempFile);
var deserializer = new XmlSerializer(typeof(Bookings));
var xml = deserializer.Deserialize(reader);
var studentBookings = (Bookings) xml;
Model of Bookings
public class Bookings
{
[XmlElement("row")]
public List<Booking> StudentBookings { get; set; }
}
Thank you
XmlSerializer doesn't allow for dynamic root names out of the box. The expected root name is fixed, and is defined by the type name, the presence of an [XmlRoot] or [XmlType] attribute, and possibly the XmlSerializer constructor used.
Thus an easy solution is to pre-load the XML into an XDocument, modify the root name, and then deserialize:
XDocument doc;
using (var xmlReader = XmlReader.Create(tempFile))
{
doc = XDocument.Load(xmlReader);
}
var rootName = doc.Root.Name;
doc.Root.Name = "Bookings";
var deserializer = new XmlSerializer(typeof(Bookings));
Bookings bookings;
using (var xmlReader = doc.CreateReader())
{
bookings = (Bookings)deserializer.Deserialize(xmlReader);
}
bookings.RootName = rootName.LocalName;
Note the use of CreateReader() to deserialize directly from the loaded XML.
Another option would be to create a custom XmlSerializer for each file using new XmlSerializer(typeof(Bookings), new XmlRootAttribute { ElementName = rootName }), however there are two issues with this:
Each serializer so created must be cached in a hash table to prevent a severe memory leak (loading of duplicate dynamically created assemblies), as is explained in this answer and also the documentation.
But even if you do this, if you are reading many different files with many different root names, your memory use will grow rapidly and continually due to constant loading of new unique dynamically created XmlSerializer assemblies.
However, if you only had to load one single file, you could do something like this:
Bookings bookings = null;
using (var xmlReader = XmlReader.Create(tempFile))
{
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
var rootName = xmlReader.LocalName;
var deserializer = new XmlSerializer(typeof(Bookings), new XmlRootAttribute { ElementName = rootName });
bookings = (Bookings)deserializer.Deserialize(xmlReader);
bookings.RootName = rootName;
break;
}
}
}
If both solutions require too much memory, you could consider creating a subclassed XmlTextReader that allows for the the root element to be renamed on the fly while reading.
In both cases I modified Bookings to look as follows:
public class Bookings
{
[XmlIgnore]
public string RootName { get; set; }
[XmlAttribute("company")]
public string Company { get; set; }
[XmlElement("row")]
public List<Booking> StudentBookings { get; set; }
}

C# - Clean way for reading data from DataSet created from XML

I've got an app with config.xml file added for user settings.
I am reading it simply by:
DataSet config = new DataSet();
config.ReadXml(configPath);
Parameters in config.xml are in columns grouped under some tables:
<?xml version="1.0" standalone="yes"?>
<UserSetup>
<TableA>
<revBegin>value1</revBegin>
<revEnd>value2</revEnd>
...
</TableA>
...
</UserSetup>
What I'm looking for is a clean way to read from DataSet config, without memorizing table or column names.
In my current implementation I achieved that by following class:
public static class MyTables
{
public static class TableA
{
public const String name = "TableA";
public const String revBegin = "revBegin";
public const String revEnd = "revEnd";
...
}
...
}
And I read values like this:
String revEnd = config.Tables[MyTables.TableA.name].Rows[0][MyTables.TableA.revEnd].ToString();
But I somehow fill that it is quite easy problem solved in quite complicated - not to say nasty - way.
Do you have any idea how can I make things simpler or cleaner?
P.S. At some point I tried reducing config.Tables[MyTables.TableA.name] part to config.Tables[MyTables.TableA] but - how I see it - it would require adding Index[object] to sealed class DataTableCollection and overriding ToString() method in my static class - both impossible. Am I right?
Unless you absolutely have to use a DataSet to read the XML, you can achieve the same results using XML serialization. Annotate your classes with the below.
[XmlRoot]
[Serializable]
public class UserSetUp
{
[XmlElement]
public TableA TableA { get; set; }
}
[Serializable]
public class TableA
{
[XmlElement]
public string revBegin { get; set; }
[XmlElement]
public string revEnd { get; set; }
}
Assuming your config in on C:\ for this example.
var configStream = File.OpenRead(#"C:\Config.xml");
var reader = new StreamReader(configStream);
var xmlString = reader.ReadToEnd();
var serializer = new XmlSerializer(typeof(UserSetUp));
var stringReader = new StringReader(xmlString);
var userSetup = (UserSetUp)serializer.Deserialize(stringReader);
I've tested it with the below XML and it works ok.
<?xml version="1.0" encoding="utf-16" ?>
<UserSetUp>
<TableA>
<revBegin>1</revBegin>
<revEnd>2</revEnd>
</TableA>
</UserSetUp>
Hope that helps you on your way.

Reading XML with records with different columns in same records array

I need to parse a XML response in C# and load in SQL.
Just to brief,i know how to use XMLSerializer to parse the xml, so that is not i am looking for. My concern is my XML structure which i received from from web request. Below is the subset of xml, i received from xml
<apiXML>
<recordList>
<record id="31" >
<administration_name>admin1</administration_name>
<creator>Leekha, Mohit</creator>
<object_category>painting</object_category>
<object_number>1243</object_number>
<id>31</id>
<reproduction.reference>2458.jpg</reproduction.reference>
<title lang="nl-NL" invariant="false">The Title1</title>
<title lang="nl-NL" invariant="false">The Title</title>
<title lang="nl-NL" invariant="false">Different Title</title>
</record>
<record id="32" >
<administration_name>admin1</administration_name>
<creator>Leekha, Mohit</creator>
<object_category>painting</object_category>
<object_number>AL1111</object_number>
<id>32</id>
<reproduction.reference>AL1111.jpg</reproduction.reference>
<title lang="nl-NL" invariant="false">Health</title>
</record>
<record id="34" >
<administration_name>admin2</administration_name>
<creator>Leekha,Mohit</creator>
<creator>System</creator>
<object_category>earthenware</object_category>
<object_category>ABC</object_category>
<object_category>Remote</object_category>
<object_number>Z.567 & X-124</object_number>
<id>34</id>
<reproduction.reference>Z.567 & X-124(1).jpg</reproduction.reference>
<reproduction.reference>Z.567 & X-124(2).jpg</reproduction.reference>
<reproduction.reference>Z.567 & X-124(3).jpg</reproduction.reference>
</record>
</recordList>
</apiXML>
My Concerns:
Some records have multiple data members with same name. Like record id 31 has 3 titles
Number of columns are different for each record.
So all i am asking is for suggestions how could i deal with scenario. Any suggestions are welcome
You'll need a couple of supporting classes to get that XML deserialized as is, as you didn't specify any other requirements.
Your database wold have tables for your record elements and all the collections within.
Serialization classes
Those classes will hold an in memory representation of your XML. At the root will be the Api class.
[XmlRoot("apiXML")]
public class Api
{
[XmlArray("recordList")]
[XmlArrayItem("record", typeof(Record))]
public List<Record> RecordList {get;set;}
}
[Serializable]
public class Record
{
[XmlAttribute("id")]
public int RecordId {get;set;}
[XmlElement("id")]
public int Id {get;set;}
[XmlElement("administration_name")]
public string AdministrationName {get;set;}
[XmlElement("object_number")]
public string ObjectNumber {get;set;}
[XmlElement("creator")]
public List<Creator> Creators {get;set;}
[XmlElement("object_category")]
public List<ObjectCategory> ObjectCategories {get;set;}
[XmlElement("reproduction.reference")]
public List<ReproductionReference> ReproductionReferences {get;set;}
[XmlElement("title")]
public List<Title> Titles {get;set;}
}
[Serializable]
public class Title:Child
{
[XmlAttribute("invariant")]
public bool Invariant {get;set;}
[XmlAttribute("lang")]
public string Culture {get;set;}
[XmlText]
public string Text {get;set;}
}
public class Child
{
[XmlIgnore]
public int ParentId {get;set;}
}
[Serializable]
public class Creator:Child
{
[XmlText]
public string Text {get;set;}
}
[Serializable]
public class ObjectCategory:Child
{
[XmlText]
public string Text {get;set;}
}
[Serializable]
public class ReproductionReference:Child
{
[XmlText]
public string Text {get;set;}
}
Deserialization
With the classes correctly annotated deserializing the XML only needs a couple
of lines:
var ser = new XmlSerializer(typeof(Api));
var sr = new StringReader(xml);
var api = (Api) ser.Deserialize(sr);
Processing and building up tables
In the variable api we now have the in-memory object graph which you can project on a relational database schema. For normalized model you'll need the following tables:
Record(id, [fields in class])
Creator(id, ..)
Title(id, ...)
ObjectCategory(id, ...)
ObjectNumber (id, ...)
ReproductionReference(id, ...)
Between these tables you'll need link tables that follow all the same convention like the one between Record and Creator:
RecordCreator(RecordId, CreatorId)
I'll assume you know how to create those tables and create a connection to your database.
// use an SqlAdapter.Fill to get the below dataset call
// sqlAdapter.Fill(ds);
var ds = new DataSet();
// this is here so you can test without a database
// test mocking code
var recTable = ds.Tables.Add("Record");
recTable.Columns.Add("Id");
recTable.Columns.Add("AdministrationName");
recTable.Columns.Add("ObjectNumber");
var creTable = ds.Tables.Add("Creator");
creTable.Columns.Add("Id", typeof(int)).AutoIncrement = true;
creTable.Columns.Add("Text");
var reccreTable = ds.Tables.Add("RecordCreator");
reccreTable.Columns.Add("RecordId");
reccreTable.Columns.Add("CreatorId");
// end mocking code
// copy object graph and build link tables
foreach(var record in api.RecordList)
{
// each main record is created
var rtRow = recTable.NewRow();
rtRow["Id"] = record.Id;
rtRow["AdministrationName"] = record.AdministrationName;
rtRow["ObjectNumber"] = record.ObjectNumber;
recTable.Rows.Add(rtRow);
// handle each collection
foreach(var creator in record.Creators)
{
DataRow creRow; // will hold our Creator row
// first try to find if the Text is already there
var foundRows = creTable.Select(String.Format("Text='{0}'", creator.Text));
if (foundRows.Length < 1)
{
// if not, add it to the Creator table
creRow = creTable.NewRow(); // Id is autoincrement!
creRow["Text"] = creator.Text;
creTable.Rows.Add(creRow);
}
else
{
// otherwise, we found an existing one
creRow = foundRows[0];
}
// link record and creator
var reccreRow = reccreTable.NewRow();
reccreRow["RecordId"] = record.Id;
reccreRow["CreatorId"] = creRow["Id"];
reccreTable.Rows.Add(reccreRow);
}
// the other collections follow a similar pattern but is left for the reader
}
// now call Update to write the changes to the db.
// SqlDataAdapter.Update(ds);
That concludes the code and structure you'll need to store that SQL in an RDBMS database without losing information.

Deserialize XML element from InnerText and Value attribute

I have XML Serializable class with property Name
[Serializable]
public class Item
{
[XmlElement("name")]
public string Name { get; set; }
}
and I want it to be able to deserialize XML file that I have in two ways:
<item>
<name>Name</name>
</item>
and
<item>
<name value="Name" />
</item>
The first works fine but what should I do to be able to deserialize the second also with the same class?
XML Serialization attributes work both with serialization and deserialization. If we'll assume that it might be possible to use attributes for deserializing instance of Item from two different xml structures, then how serialization should work - should it serialize instance name to element value, or to attribute? Or to both? That's why you cannot deserialize two different xml structures into single class. Use two different classes or deserialize it manually without usage of XML Serialization attributes.
I found another way to solve my problem using only one class maybe someone will find this useful
[Serializable]
public class Item
{
[XmlElement("name")]
public NameElement NameElement { get; set; }
}
public class NameElement
{
[XmlAttribute("value")]
public string Value { get; set; }
[XmlText]
public string Text { get; set; }
[XmlIgnore]
public string Name
{
get { return String.IsNullOrEmpty(this.Value) ? this.Text : this.Value; }
set { this.Value = value; }
}
}
Maybe it's not super elegant but it works in both cases and uses the same class.
Since you have mentioned that XML data is coming from external sources, so obviously you don't have control over that.
Therefore you can follow any of the option as below:
Create separate class per XML data structure, because as far I know there is no way to control XML Deserialization when using XmlSerializer
You can use XDocument to read the XML by self, to overcome this limitation.
If going by second idea, I have created small Console Application to demonstrate that.
Main piece of code is as below:
MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData));
XDocument doc = XDocument.Load(xmlStream);
var records = from record in doc.Descendants("item").Descendants()
select new Item(!record.IsEmpty ? record.Value : record.Attribute("value").Value);
Here I'm reading the element using LinqToXml and checking if the element is not empty, i.e. Value is not blank, then use Value otherwise read the value from element's Attribute.
Console application (Complete code):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace Console.TestApp
{
class Program
{
static string xmltypeFirst = #"<item>
<name>John</name>
</item>";
static string xmltypeSecond = #"<item>
<name value='Smith' />
</item>";
static void Main(string[] args)
{
var data = xmltypeFirst;
var result = Deserialize(data).ToList();
Console.WriteLine("Name: " + result[0].Name);
data = xmltypeSecond;
result = Deserialize(data).ToList();
Console.WriteLine("Name: " + result[0].Name);
Console.WriteLine("Press any to key to exit..");
Console.ReadLine();
}
private static IEnumerable<Item> Deserialize(string xmlData)
{
MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData));
XDocument doc = XDocument.Load(xmlStream);
var records = from record in doc.Descendants("item").Descendants()
select new Item(!record.IsEmpty ? record.Value : record.Attribute("value").Value);
return records;
}
}
[Serializable]
public class Item
{
public Item(string name)
{
this.Name = name;
}
[XmlElement("name")]
public string Name { get; set; }
}
}
Note: To run this you will need to add reference to System.Xml.Linq.dll in your project.
Reference: here

Categories

Resources