C# - How to serialize and derialize DataSet correctly? - c#

I cannot deserialize the serialized dataset correctly if it has dates. The result is the dates are considered as a separate table instead of columns itself. It seems that it is not parsed correctly. How do I fix this?
Here are the variables under inspection after the serialization and deserialization. You can see that the deserializedDataSet.Tables has 2 counts where its original is just 1 count. I printed the table names and shows that the second table was date. Moreover, I can access the date column in the original data set but unable to access in the deserialized version.
Here is the serialized data of the dataset.
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<producttransactionsummary>
<id>74517</id>
<branchid>9999</branchid>
<date xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Year>2016</Year>
<Month>11</Month>
<Day>10</Day>
<Hour>18</Hour>
<Minute>25</Minute>
<Second>40</Second>
<Millisecond>0</Millisecond>
</date>
</producttransactionsummary>
<producttransactionsummary>
<id>74518</id>
<branchid>9999</branchid>
<date xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Year>2016</Year>
<Month>11</Month>
<Day>10</Day>
<Hour>18</Hour>
<Minute>25</Minute>
<Second>40</Second>
<Millisecond>0</Millisecond>
</NewDataSet>
Here is the deserialized dataset.
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<producttransactionsummary>
<id>74517</id>
<branchid>9999</branchid>
<date>
<Year>2016</Year>
<Month>11</Month>
<Day>10</Day>
<Hour>18</Hour>
<Minute>25</Minute>
<Second>40</Second>
<Millisecond>0</Millisecond>
</date>
</producttransactionsummary>
<producttransactionsummary>
<id>74518</id>
<branchid>9999</branchid>
<date>
<Year>2016</Year>
<Month>11</Month>
<Day>10</Day>
<Hour>18</Hour>
<Minute>25</Minute>
<Second>40</Second>
<Millisecond>0</Millisecond>
</date>
</producttransactionsummary>
</NewDataSet>
Here is my current code:
public static class Transport
{
public static MemoryStream Serialize(DataSet dataSet)
{
var memoryStream = new MemoryStream();
dataSet.WriteXml(memoryStream, XmlWriteMode.IgnoreSchema);
dataSet.WriteXml("input.xml");
memoryStream.Flush();
memoryStream.Position = 0;
return memoryStream;
}
public static DataSet Deserialize(MemoryStream stream)
{
var dataSet = new DataSet();
dataSet.ReadXml(stream);
dataSet.WriteXml("output.xml");
return dataSet;
}
}

Related

Load XML containing multiple lists to multiple DataTables

I have an XML that has the below structure:
<?xml version="1.0" encoding="utf-8"?>
<n0:Response xmlns:n0="urn:sap-com:document:sap:rfc:functions">
<Cars>
<item>
<ID>1</ID>
<TYPE>SUV</TYPE>
<YEAR>2022</YEAR>
</item>
<item>
<ID>2</ID>
<TYPE>Convertible</TYPE>
<YEAR>2021</YEAR>
</item>
</Cars>
<Employees>
<item>
<ID>1</ID>
<NAME>John</NAME>
<STATUS>1</STATUS>
<PHONE>000000000</PHONE>
</item>
<item>
<ID>2</ID>
<NAME>Sam</NAME>
<STATUS>2</STATUS>
<PHONE>000000000</PHONE>
</item>
<item>
<ID>3</ID>
<NAME>Jane</NAME>
<STATUS>1</STATUS>
<PHONE>000000000</PHONE>
</item>
</Employees>
</n0:Response>
(this is just a sample, the final file will have more than 10k items per row)
I need to load that file to two DataTables, each having the correct values.
I do this by creating DataSet, loading XML, and filtering the data table.
My DataSet has 3 tables:
but when I try to access the correct data table I get all the items instead of the correct ones (I get items from the entire XML):
var rel = ds.Relations.OfType<DataRelation>()
.FirstOrDefault(x => x.ParentTable.TableName == "Cars")?.ChildTable;
this is the result:
I managed to filter the data by adding a Select statement like below:
var ds = new DataSet();
using Stream stream = new FileStream("Sample.xml", FileMode.Open, FileAccess.Read);
ds.ReadXml(stream);
var table1 = ds.Relations.OfType<DataRelation>()
.FirstOrDefault(x => x.ParentTable.TableName == "Cars")?
.ChildTable.Select("Cars_Id = 0").CopyToDataTable();
var table2 = ds.Relations.OfType<DataRelation>()
.FirstOrDefault(x => x.ParentTable.TableName == "Employees")?
.ChildTable.Select("Employees_Id = 0").CopyToDataTable();
but this isn't generic and I'd like to be able to access the correct DataTable only by name (the node name from XML).
My question is similar to How to get multiple tables in Dataset from XML, but the provided answer didn't solve the issue.

Can you Serialize inside a set of Attributes by Set of Attributes?

I want a serialize a set of attributes all together to an XML file in a loop that runs. This loop runs within a set of folders . According to my Current implementation It only shows the last set of Attributes.
Here is my Current code ,
Student obj = JsonConvert.DeserializeObject < StudentModel (File.ReadAllText(file));
string Name = studentModel.name;
string number = studentModel.number;
string testPath = rootDirectory;
XmlSerializer serializer = new XmlSerializer(typeof(StudentModel));
System.IO.StreamWriter writer = new System.IO.StreamWriter(testPath + "\\Test.xml");
serializer.Serialize(writer, studentModel);
writer.Close();
My Current output is
<?xml version="1.0" encoding="utf-8"?>
<StudentModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>Max</name>
<number>1</number>
</StudentModel>
Required output is
<?xml version="1.0" encoding="utf-8"?>
<StudentModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>Max</name>
<number>1</number>
</StudentModel>
<StudentModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>Amy</name>
<number>2</number>
</StudentModel>
<StudentModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>Jack</name>
<number>3</number>
</StudentModel>
It seems to me, that what you want to do is serialize a list (or array) of StudentModel to an xml. Currently you are only serializing one instance of StudentModel and overwrite the resulting xml each iteration of the loop, leaving you with the last item. What you can do, is to create a class with a List() of StudentModel as Property and serialize that class instance, not needing a loop anymore.
The List property in your new class would then look something like this:
[XmlArray("StudentModels"), XmlArrayItem(typeof(StudentModel), ElementName = "StudentModel")]
public List<StudentModel> StudentModelList{ get; set; }

Create XML file from collection list

I have a collection string with information about customers like name, gender etc. All custumers have an id.
Now i want to create a common XML file with all customers in it. Something like example below:
<custumers>
<custumer>
<name></name>
<id></id>
<etc></etc>
</custumer>
</custumers>
The start up with no XML file is easy, I used linq to create the xml file.
For initial creation I used following code:
try
{
var xEle = new XElement("Customers",
from cus in cusList
select new XElement("Customer",
new XElement("Name", cus.Name),
new XElement("gender", cus.gender),
new XElement("etc", cus.etc));
}
xEle.Save(path);
But on the point if i want to update the XML file i get some problems to get it.
My approach to solve it:
Iterate over all customers in list and check for all customers if the customer.id exists in the XML.
IF not: add new customer to xml
IF yes: update values
My code so far:
var xEle = XDocument.Load(xmlfile);
foreach (cus in cusList)
try
{
var cids = from cid in xEle.Descendants("ID")
where Int32.Parse(xid.Element("ID").Value) == cus.ID
select new XElement("customer", cus.name),
new XElement ("gender"), cus.gender),
new XELement ("etc."), cus.etc)
);
xEle.Save(xmlpath);
}
How about a different approach....
using System.IO;
using System.Text;
using System.Xml.Serialization;
public void Main()
{
DataSet Custumers = new DataSet("Custumers");
DataTable Custumer = new DataTable("Custumer");
// Create the columns
Custumer.Columns.Add("id", typeof(int)).Unique = true;
Custumer.Columns.Add("name", typeof(string));
Custumer.Columns.Add("gender", typeof(string));
Custumer.Columns.Add("etc", typeof(string));
// Set the primary key
Custumer.PrimaryKey = { Custumer.Columns("id") };
// Add table to dataset
Custumers.Tables.Add(Custumer);
Custumers.AcceptChanges();
// Add a couple of rows
Custumer.Rows.Add(1, "John", "male", "whatever");
Custumer.Rows.Add(2, "Jane", "female", "whatever");
Custumer.AcceptChanges();
// Let's save this to compare to the updated version
Custumers.WriteXml("Custumers_Original.xml");
// Read in XML that contains an existing Custumer and adds a new one
// into a Clone of the Custumer table
DataTable CustumerUpdate = Custumer.Clone;
CustumerUpdate.ReadXml("Custumers_Update.xml");
// Merge the clone table data to the Custumer table
Custumer.Merge(CustumerUpdate);
Custumer.AcceptChanges();
Custumers.WriteXml("Custumers_Final.xml");
}
Custumers_Original.xml looks like this:
<?xml version="1.0" standalone="yes"?>
<Custumers>
<Custumer>
<id>1</id>
<name>John</name>
<gender>male</gender>
<etc>whatever</etc>
</Custumer>
<Custumer>
<id>2</id>
<name>Jane</name>
<gender>female</gender>
<etc>whatever</etc>
</Custumer>
</Custumers>
Custumers_Update.xml has this, making a change to John and adding George:
<?xml version="1.0" encoding="utf-8" ?>
<Custumers>
<Custumer>
<name>John</name>
<id>1</id>
<gender>male</gender>
<etc>this is new</etc>
</Custumer>
<Custumer>
<name>George</name>
<id>3</id>
<gender>male</gender>
<etc>grandson</etc>
</Custumer>
</Custumers>
After the merge, the Custumers_Final.xml contains this:
<?xml version="1.0" standalone="yes"?>
<Custumers>
<Custumer>
<id>1</id>
<name>John</name>
<gender>male</gender>
<etc>this is new</etc>
</Custumer>
<Custumer>
<id>2</id>
<name>Jane</name>
<gender>female</gender>
<etc>whatever</etc>
</Custumer>
<Custumer>
<id>3</id>
<name>George</name>
<gender>male</gender>
<etc>grandson</etc>
</Custumer>
</Custumers>
Also my solution with linq:
try
{
XDocument xEle = XDocument.Load(path);
var ids = from id in xEle.Descendants("Custom")
where id.Element("id").Value == cus.ID
select id;
foreach (XElement idCustom in ids)
{
idCustom.SetElementValue("NewName", "NewElement");
}
xEle.Save(path);
}

StringWriter.ToString() is breaking xml C#

I have a class array that I'm going to pass to XML to send it later to SQL Server Stored Procedure.
The problem is that when I execute StringWriter it adds some scape caracters (=\) that appears in the final XML.
My code is the following:
public XmlDocument ToXML(object obj_to_xml)
{
string str_XML;
XmlDocument xml = new XmlDocument();
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(obj_to_xml.GetType());
serializer.Serialize(stringwriter, obj_to_xml);**
//at this point the format is correct, for example stringwriter contains:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfRegistroPrestamo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RegistroPrestamo>
<Id>1</Id>
<Cedula>8-834-789</Cedula>
<Nombre>Amarillos</Nombre>
<Apellido>Perez</Apellido>
</RegistroPrestamo>
<RegistroPrestamo>
<Id>0</Id>
<Cedula />
<Nombre />
<Apellido />
</RegistroPrestamo>
</ArrayOfRegistroPrestamo>
//the problem comes with this line
xml.LoadXml(stringwriter.ToString());
//final XML looks like this:
<?xml version=\"1.0\" encoding=\"utf-16\"?>
<ArrayOfRegistroPrestamo xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
<RegistroPrestamo>
<Id>1</Id>
<Cedula>8-834-789</Cedula>
<Nombre>Amarillos</Nombre>
<Apellido>Perez</Apellido>
</RegistroPrestamo>
<RegistroPrestamo>
<Id>0</Id>
<Cedula />
<Nombre />
<Apellido />
</RegistroPrestamo>
</ArrayOfRegistroPrestamo>
return xml;
}
When SQL Server tries to parse the XML Code:
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "P_Save_Procedure";
cmd.Parameters.Add("#Registros", SqlDbType.Xml).Value = xml;
Stored Procedure Text
Create procedure [dbo].[P_Save_Procedure]
(#Registros as xml)
as
begin
select distinct 'Id' = x.v.value('Id[1]', 'Int')
from #Registros.nodes('/ArrayOfRegistroPrestamo/RegistroPrestamo') x(v)
end
I got the error:
Mens. 9413, Level 16, State 1, Line 4
XML parsing: line 1, character 37; It expected a string literal
If I call the SP manually with the correct version of the XML:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfRegistroPrestamo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RegistroPrestamo>
<Id>1</Id>
<Cedula>8-834-789</Cedula>
<Nombre>Amarillos</Nombre>
<Apellido>Perez</Apellido>
</RegistroPrestamo>
<RegistroPrestamo>
<Id>0</Id>
<Cedula />
<Nombre />
<Apellido />
</RegistroPrestamo>
</ArrayOfRegistroPrestamo>
The result is the expected.
Already have spend 2 days tryng to find a way to get the correct sintax for the XML and got nothing.
See this:
DoNotEscapeUriAttributes

C#, XML - Loading a single XML file into two separate datasets based on a parameter

I need to load XML data into two separate datasets based on a given parameter within the XML. The data is fed back to me via a SOAP call.
Here is a very simplified XML sample:
<![CDATA[<?xml version="1.0" encoding="utf-16"?>
<ArrayOfUser xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<User>
<ID>111-111-111</ID
<Name>John Smith</Name>
<Active>0</Active>
</User>
<User>
<ID>111-111-222</ID
<Name>Bob Smith</Name>
<Active>0</Active>
</User>
<User>
<ID>111-111-333</ID
<Name>Sally Smith</Name>
<Active>1</Active>
</User>
</ArrayOfUser>]]>
Right now, I load everything into a single dataset as such:
XmlDocument UsersXmlDoc= new XmlDocument();
UsersXmlDoc.LoadXml(GetUsersResponse.Fetch_Result);
XmlReader UsersXmlReader= new XmlNodeReader(UsersXmlDoc);
DataSet ds = new DataSet();
ds.ReadXml(UsersXmlReader);
I would like instead to split that information into two datasets based on the Active property of each user so that one dataset contains active uses and the other, innactive users.
I made an example how you could do what you want. Maybe code isn't very nice, but I think it should work. The main idea is to copy current dataset and remove active records from first and inactive records from second dataset.
DataSet dsActive = new DataSet();
dsActive.ReadXml(UsersXmlReader);
DataSet dsInactive = ds.Copy();//Copy to your another dataset
Remove(dsActive.Tables[0], "0");//Remove all inactive(where Active = 0) records from dsActive DataSet
Remove(dsInactive.Tables[0], "1");//Remove all active(where Active = 1) records from dsInactive DataSet
private void Remove(DataTable table, string active)
{
for (int i = 0; i < table.Rows.Count; i++)
{
if (table.Rows[i]["Active"].Equals(active))
{
table.Rows[i].Delete();
i = i - 1;//Removed record so we need to check same index
}
}
}

Categories

Resources