How to deserialize XML into an object in C#? [duplicate] - c#

This question already has answers here:
How to Deserialize XML document
(16 answers)
Closed 2 years ago.
I need to read XML and deserialize it into an object in C#. I created the file with the serialization part of the ToXmlFile(Object obj, string filePath) function as given further down. I have the following XML created in the file test.xml:
<NYSE_Daily_Prices>
<stock_exchange>NYSE</stock_exchange>
<stock_symbol>ADI</stock_symbol>
<date>2000-01-03T00:00:00</date>
<stock_price_open>93.5</stock_price_open>
<stock_price_high>93.87</stock_price_high>
<stock_price_low>88</stock_price_low>
<stock_price_close>90.19</stock_price_close>
<stock_volume>3655600</stock_volume>
<stock_price_adj_close>39.97</stock_price_adj_close>
</NYSE_Daily_Prices>
<NYSE_Daily_Prices>
<stock_exchange>NYSE</stock_exchange>
<stock_symbol>ADI</stock_symbol>
<date>2000-01-04T00:00:00</date>
<stock_price_open>89.5</stock_price_open>
<stock_price_high>91.5</stock_price_high>
<stock_price_low>85.56</stock_price_low>
<stock_price_close>85.62</stock_price_close>
<stock_volume>2533200</stock_volume>
<stock_price_adj_close>37.95</stock_price_adj_close>
</NYSE_Daily_Prices>
<NYSE_Daily_Prices>
<stock_exchange>NYSE</stock_exchange>
<stock_symbol>ADI</stock_symbol>
<date>2000-01-05T00:00:00</date>
<stock_price_open>85.62</stock_price_open>
<stock_price_high>88.25</stock_price_high>
<stock_price_low>83.19</stock_price_low>
<stock_price_close>86.88</stock_price_close>
<stock_volume>3228000</stock_volume>
<stock_price_adj_close>38.51</stock_price_adj_close>
</NYSE_Daily_Prices>
Here is my object:
public partial class NYSE_Daily_Prices
{
public string stock_exchange { get; set; }
public string stock_symbol { get; set; }
public System.DateTime date { get; set; }
public double stock_price_open { get; set; }
public double stock_price_high { get; set; }
public double stock_price_low { get; set; }
public double stock_price_close { get; set; }
public int stock_volume { get; set; }
public double stock_price_adj_close { get; set; }
}
And this is the code to serialize/deserialize it:
public static class XmlHelper
{
public static bool NewLineOnAttributes { get; set; }
/// <summary>
/// Serializes an object to an XML string, using the specified namespaces.
/// </summary>
public static string ToXml(object obj, XmlSerializerNamespaces ns)
{
Type T = obj.GetType();
var xs = new XmlSerializer(T);
var ws = new XmlWriterSettings { Indent = true, NewLineOnAttributes = NewLineOnAttributes, OmitXmlDeclaration = true };
var sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, ws))
{
xs.Serialize(writer, obj, ns);
}
return sb.ToString();
}
/// <summary>
/// Serializes an object to an XML string.
/// </summary>
public static string ToXml(object obj)
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
return ToXml(obj, ns);
}
/// <summary>
/// Deserializes an object from an XML string.
/// </summary>
public static T FromXml<T>(string xml)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(xml))
{
return (T)xs.Deserialize(sr);
}
}
/// <summary>
/// Deserializes an object from an XML string, using the specified type name.
/// </summary>
public static object FromXml(string xml, string typeName)
{
Type T = Type.GetType(typeName);
XmlSerializer xs = new XmlSerializer(T);
using (StringReader sr = new StringReader(xml))
{
return xs.Deserialize(sr);
}
}
/// <summary>
/// Serializes an object to an XML file.
/// </summary>
public static void ToXmlFile(Object obj, string filePath)
{
var xs = new XmlSerializer(obj.GetType());
var ns = new XmlSerializerNamespaces();
var ws = new XmlWriterSettings { Indent = true, NewLineOnAttributes = NewLineOnAttributes, OmitXmlDeclaration = true };
ns.Add("", "");
using (XmlWriter writer = XmlWriter.Create(filePath, ws))
{
xs.Serialize(writer, obj);
}
}
/// <summary>
/// Deserializes an object from an XML file.
/// </summary>
public static T FromXmlFile<T>(string filePath)
{
StreamReader sr = new StreamReader(filePath);
try
{
var result = FromXml<T>(sr.ReadToEnd());
return result;
}
catch (Exception e)
{
throw new Exception("There was an error attempting to read the file " + filePath + "\n\n" + e.InnerException.Message);
}
finally
{
sr.Close();
}
}
}
And the code I use to request the deserialization:
string filePath = $#"C:\Users\dmast\Documents\Upskilled\C#\C# Programming Project\MoneyBMineWpfApp\MoneyBMineWpfApp\OfflineFilesXML\{listRecentSearches.SelectedItem.ToString()}";
NYSE_Daily_Prices result = XMLReadWrite.XmlHelper.FromXmlFile<NYSE_Daily_Prices>(filePath);
I keep getting the caught exception:
catch (Exception e)
{
throw new Exception("There was an error attempting to read the file " + filePath + "\n\n" + e.InnerException.Message);
}
Any help would be much appreciated :)

Davide. Your code looks fine to me. I tried it locally.
Are you sure, that you provide the right path to the file?
I changed a little xml, because I assume it should have a root element. Now .xml file looks like this:
<root>
<Prices>
<NYSE_Daily_Prices>
<stock_exchange>NYSE</stock_exchange>
<stock_symbol>ADI</stock_symbol>
<date>2000-01-03T00:00:00</date>
<stock_price_open>93.5</stock_price_open>
<stock_price_high>93.87</stock_price_high>
<stock_price_low>88</stock_price_low>
<stock_price_close>90.19</stock_price_close>
<stock_volume>3655600</stock_volume>
<stock_price_adj_close>39.97</stock_price_adj_close>
</NYSE_Daily_Prices>
<NYSE_Daily_Prices>
<stock_exchange>NYSE</stock_exchange>
<stock_symbol>ADI</stock_symbol>
<date>2000-01-04T00:00:00</date>
<stock_price_open>89.5</stock_price_open>
<stock_price_high>91.5</stock_price_high>
<stock_price_low>85.56</stock_price_low>
<stock_price_close>85.62</stock_price_close>
<stock_volume>2533200</stock_volume>
<stock_price_adj_close>37.95</stock_price_adj_close>
</NYSE_Daily_Prices>
<NYSE_Daily_Prices>
<stock_exchange>NYSE</stock_exchange>
<stock_symbol>ADI</stock_symbol>
<date>2000-01-05T00:00:00</date>
<stock_price_open>85.62</stock_price_open>
<stock_price_high>88.25</stock_price_high>
<stock_price_low>83.19</stock_price_low>
<stock_price_close>86.88</stock_price_close>
<stock_volume>3228000</stock_volume>
<stock_price_adj_close>38.51</stock_price_adj_close>
</NYSE_Daily_Prices>
</Prices>
</root>
I created 1 more class Root:
[XmlRoot("root")]
public class Root
{
[XmlArray("Prices")]
[XmlArrayItem("NYSE_Daily_Prices")]
public List<NYSE_Daily_Prices> Prices { get; set; } = new List<NYSE_Daily_Prices>();
}
Tested this code in console application, it works fine:
class Program
{
static void Main(string[] args)
{
var deserializedObject = XmlHelper.FromXmlFile<Root>(Environment.CurrentDirectory + #"\file.xml");
}
}
I hope it helps!

Related

System.InvalidOperationException : <LicenseBase xmlns=''> was not expected

I try to serialize and deserialize the class License.
The serialization work well but when I try to deserialize the file
I get the error message above.
This is the base class:
[Serializable]
public abstract class LicenseBase
{
/// <summary>
/// Initializes a new instance of the <see cref="LicenseBase"/> class.
/// </summary>
protected LicenseBase()
{
ApplicationName = string.Empty;
UniqueId = string.Empty;
}
/// <summary>
/// Application name this license is used for
/// </summary>
[Browsable(false)]
public string ApplicationName { get; set; }
/// <summary>
/// Unique hardware id this license will work on
/// </summary>
[Browsable(false)]
public string UniqueId { get; set; }
}
And this the derived:
public class License : LicenseBase
{
[Browsable(false)]
public bool Test { get; set; }
}
This is the method to serialize the class:
public void WriteLicense<T>(T license) where T : LicenseBase
{
if (license is null)
{
throw new ArgumentNullException(nameof(license));
}
//Serialize license object into XML
XmlDocument licenseObject = new XmlDocument();
using (StringWriter writer = new StringWriter())
{
XmlSerializer serializer = new XmlSerializer(typeof(LicenseBase), new[]
{
license.GetType(), typeof(License)
});
serializer.Serialize(writer, license);
licenseObject.LoadXml(writer.ToString());
}
//Sign the XML
SignXml(licenseObject);
//Convert the signed XML into BASE64 string
string writeToFile = Convert.ToBase64String(Encoding.UTF8.GetBytes(licenseObject.OuterXml));
File.WriteAllText(LICENSE_FILENAME, writeToFile);
}
This is the code to read the class:
public T ReadLicense<T>() where T : LicenseBase
{
T license;
if (!File.Exists(LICENSE_FILENAME))
{
alarmManager.ReportAlarm(licenseFileMissingAlarm, true, true);
return null;
}
string licenseFileData = File.ReadAllText(LICENSE_FILENAME);
XmlDocument xmlDoc = new XmlDocument { PreserveWhitespace = true };
xmlDoc.LoadXml(Encoding.UTF8.GetString(Convert.FromBase64String(licenseFileData)));
// Verify the signature of the signed XML.
if (VerifyXml(xmlDoc, PrivateKey))
{
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");
if (xmlDoc.DocumentElement != null)
{
_ = xmlDoc.DocumentElement.RemoveChild(nodeList[0]);
}
string licenseContent = xmlDoc.OuterXml;
//Deserialize license
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringReader reader = new StringReader(licenseContent))
{
license = (T)serializer.Deserialize(reader);
}
}
else
{
license = null;
}
return license;
}
The content of licenseContent is
<?xml version="1.0" encoding="UTF-8"?>
<LicenseBase xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="License">
<ApplicationName>Test</ApplicationName>
<UniqueId>c4aed5a8-8d22-47b0-bda4-700ac906bfd5</UniqueId>
<Test>true</Test>
</LicenseBase>
I solve the problem this was the failure;
XmlSerializer serializer = new XmlSerializer(typeof(LicenseBase), new[]
{
license.GetType(), typeof(License)
});
And this the solution:
XmlSerializer serializer = new XmlSerializer(license.GetType(), new[]
{
license.GetType()
});
After this changes everything work fine.

Object resulting from xml deserialization only has 1 element, despite xml having multiple elements

I'm trying to test the serialization for webrequests.
I'm doing a unit test where I:
- create a mock response from the server
- deserialize that response
- Compare initial object with deserialized one
The issue is one of my arrays in only partially populated where instead of all the elements, it only has one, the last one.
Deserialization has to be made by hand because of xml schema limitations.
Item is a partial class to separate the DTO from the xml operations
I have tried to change the attributes of the array property to
[XmlArray("items")]
[XmlArrayItemAttribute("item")]
I have tested just the serialization-deserialization of an individual item and it works.
I have checked both the xml resulting from the original serialization and the xml which is deseriliazed and they are equal.
First the item itself:
[XmlRoot("item")]
public partial class InvoiceItem
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
}
Now the array:
[XmlArray("items")]
[XmlArrayItemAttribute("item")]
public InvoiceItem[] InvoiceItems
{
get
{
return this.invoiceItems;
}
set
{
this.invoiceItems = value;
}
}
Finally the deserializator:
public void ReadXml(XmlReader reader)
{
Regex regexTaxName = new Regex(#"tax\d_name");
Regex regexTaxType = new Regex(#"tax\d_type");
Regex regexTaxPercent = new Regex(#"tax\d_percent");
Regex regexTaxNumber = new Regex(#"\d");
List<Tax> taxesList = new List<Tax>();
while (reader.Read())
{
Debug.WriteLine(reader.GetAttribute("name"));
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name.Equals("name", StringComparison.Ordinal))
{
reader.Read();
this.Name = reader.Value;
}
else if (reader.Name.Equals("type", StringComparison.Ordinal))
{
ProductType value = ProductType.Product;
reader.Read();
if (Enums.TryParse<ProductType>(reader.Value, out value))
{
this.Type = value;
}
}
else if (reader.Name.Equals("description", StringComparison.Ordinal))
{
reader.Read();
this.Description = reader.Value;
}
else if (reader.Name.Equals("unit_cost", StringComparison.Ordinal))
{
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
this.UnitCost = value;
}
}
else if (reader.Name.Equals("quantity", StringComparison.Ordinal))
{
int value = 0;
reader.Read();
if (int.TryParse(reader.Value, out value))
{
this.Quantity = value;
}
}
else if (reader.Name.Equals("discount", StringComparison.Ordinal))
{
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
this.Discount = value;
}
}
else if (reader.Name.Equals("discount_type", StringComparison.Ordinal))
{
NumericalSignificance value = NumericalSignificance.Percent;
reader.Read();
if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
{
this.DiscountType = value;
}
}
else if (regexTaxName.IsMatch(reader.Name))
{
int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
if (taxesList.Count < taxNumber)
{
reader.Read();
Tax newTax = new Tax();
newTax.Name = reader.Value;
taxesList.Add(newTax);
}
else
{
reader.Read();
taxesList[taxNumber-1].Name = reader.Value;
}
}
else if (regexTaxPercent.IsMatch(reader.Name))
{
int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
if (taxesList.Count > taxNumber)
{
Tax newTax = new Tax();
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
newTax.TaxPercent = value;
}
taxesList.Add(newTax);
}
else
{
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
taxesList[taxNumber-1].TaxPercent = value;
}
}
}
else if (regexTaxType.IsMatch(reader.Name))
{
int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
if (taxesList.Count > taxNumber)
{
Tax newTax = new Tax();
NumericalSignificance value = NumericalSignificance.Percent;
reader.Read();
if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
{
newTax.Type = value;
}
taxesList.Add(newTax);
}
else
{
NumericalSignificance value = NumericalSignificance.Percent;
reader.Read();
if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
{
taxesList[taxNumber-1].Type = value;
}
}
}
}
}
taxesArr = taxesList.ToArray();
}
The problems is on the items array where the end object only has the final object instead of all the original ones.
EDIT:
An example that shows the issue:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
[XmlRoot("invoice")]
public class Invoice
{
[XmlArray("items")]
private InvoiceExampleItem[] items;
[XmlArray("items")]
public InvoiceExampleItem[] Items
{
get { return this.items; }
set { this.items = value; }
}
}
[XmlRoot("item", Namespace = "")]
public partial class InvoiceExampleItem : IXmlSerializable
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name.Equals("name", StringComparison.Ordinal))
{
reader.Read();
this.Name = reader.Value;
}
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("name", this.Name);
}
}
[TestClass]
public class ExampleTest : AutoMoqTest
{
[TestMethod]
public void ExampleDeserialization()
{
InvoiceExampleItem item1 = new InvoiceExampleItem()
{
Name = "item1"
};
InvoiceExampleItem item2 = new InvoiceExampleItem()
{
Name = "item2"
};
InvoiceExampleItem item3 = new InvoiceExampleItem()
{
Name = "item3"
};
Invoice mockInvoice = new Invoice()
{
Items = new InvoiceExampleItem[] { item1, item2, item3 }
};
XmlDocument mockInvoiceSerialized = SerializeInvoice(mockInvoice);
XmlDocument mockResponseXml = GenerateXmlResponse(mockInvoiceSerialized);
GetInvoiceResponse response = DeserializeXML<GetInvoiceResponse>(mockResponseXml.OuterXml);
Invoice resultInvoice = response.Invoice;
if (mockInvoice.Items.Length != resultInvoice.Items.Length)
{
throw new Exception("wrong number of items");
}
}
public XmlDocument SerializeInvoice(Invoice invoiceToSerialize)
{
XmlDocument toReturn = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
XmlReaderSettings settings = new XmlReaderSettings();
XmlDocument itemsDocument = GetTemplateXML();
InvoiceExampleItem[] items = invoiceToSerialize.Items;
MemoryStream memStm = null, tempStream = null;
try
{
memStm = tempStream = new MemoryStream();
using (StreamWriter sw = new StreamWriter(memStm, Encoding.UTF8))
{
// Serialize object into raw xml
memStm = null;
serializer.Serialize(sw, invoiceToSerialize);
sw.Flush();
// parse raw xml into Xml document
tempStream.Position = 0;
settings.IgnoreWhitespace = true;
var xtr = XmlReader.Create(tempStream, settings);
toReturn.Load(xtr);
}
}
finally
{
if (memStm != null)
{
memStm.Dispose();
}
}
return toReturn;
}
public static T DeserializeXML<T>(string responseString)
where T : class
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(responseString))
{
return (T)xmlSerializer.Deserialize(sr);
}
}
public XmlDocument GetTemplateXML()
{
XmlDocument toReturn = new XmlDocument();
var decl = toReturn.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
toReturn.AppendChild(decl);
return toReturn;
}
private XmlDocument GenerateXmlResponse(XmlDocument innerXMLDocument)
{
XmlDocument toReturn = GetTemplateXML();
XmlElement requestElement = toReturn.CreateElement("response");
requestElement.SetAttribute("status", "success");
requestElement.InnerXml = innerXMLDocument.DocumentElement.OuterXml;
toReturn.AppendChild(requestElement);
return toReturn;
}
/// <summary>
/// Web response from creating a invoice
/// </summary>
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", ElementName = "response")]
public class GetInvoiceResponse
{
/// <summary>
/// Gets or sets the response status
/// </summary>
/// <value>
/// reponse Status
/// </value>
[XmlAttribute("status")]
public string ResponseStatus { get; set; }
/// <summary>
/// Gets or sets the new invoice id
/// </summary>
/// <value>
/// generated by invoicera for this response
/// </value>
[XmlElement(ElementName = "invoice")]
public Invoice Invoice { get; set; }
}
}
The solution was to create a class for the array and implement the IXMLSeriazable interface in that class and removing the interface from the item class.
Then, when the xml in the items class, I cycle the tags and create an item individually, adding it to the array next.
For some reason, the method was not exiting when it processed each <\item> tag, so I added a condition to exit the cycle.
Here is the complete code:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
[XmlRoot("invoice")]
public class Invoice
{
public Items items { get; set; }
}
[XmlRoot("item", Namespace = "")]
public partial class InvoiceExampleItem
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name.Equals("name", StringComparison.Ordinal))
{
reader.Read();
this.Name = reader.Value;
return;
}
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("name", this.Name);
}
}
public class Items : IXmlSerializable
{
[XmlElement("item")]
public List<InvoiceExampleItem> list = new List<InvoiceExampleItem>();
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.ReadToFollowing("item"))
{
InvoiceExampleItem currentItem = new InvoiceExampleItem();
currentItem.Name = reader.Value;
list.Add(currentItem);
}
}
public void WriteXml(XmlWriter writer)
{
foreach(InvoiceExampleItem item in list)
{
writer.WriteStartElement("item");
item.WriteXml(writer);
writer.WriteEndElement();
}
}
}
[TestClass]
public class ExampleTest : AutoMoqTest
{
[TestMethod]
public void ExampleDeserialization()
{
/**/
InvoiceExampleItem item1 = new InvoiceExampleItem()
{
Name = "item1"
};
InvoiceExampleItem item2 = new InvoiceExampleItem()
{
Name = "item2"
};
InvoiceExampleItem item3 = new InvoiceExampleItem()
{
Name = "item3"
};
Items items = new Items();
InvoiceExampleItem item21 = new InvoiceExampleItem()
{
Name = "item1"
};
InvoiceExampleItem item22 = new InvoiceExampleItem()
{
Name = "item2"
};
InvoiceExampleItem item23 = new InvoiceExampleItem()
{
Name = "item3"
};
items.list.Add(item21);
items.list.Add(item22);
items.list.Add(item23);
Invoice mockInvoice = new Invoice()
{
items = items
};
/**/
XmlDocument mockInvoiceSerialized = SerializeInvoice(mockInvoice);
XmlDocument mockResponseXml = GenerateXmlResponse(mockInvoiceSerialized);
GetInvoiceResponse test = new GetInvoiceResponse();
GetInvoiceResponse response = DeserializeXML<GetInvoiceResponse>(mockResponseXml.OuterXml);
Invoice resultInvoice = response.Invoice;
mockResponseXml.Save("C:\\Users\\360Imprimir\\Desktop\\mockResponseXml");
mockInvoiceSerialized.Save("C:\\Users\\360Imprimir\\Desktop\\mockInvoiceSerialized.Xml");
if (mockInvoice.items.list.Count != resultInvoice.items.list.Count)
{
throw new Exception("wrong number of items");
}
}
public XmlDocument SerializeInvoice(Invoice invoiceToSerialize)
{
XmlDocument toReturn = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
XmlReaderSettings settings = new XmlReaderSettings();
XmlDocument itemsDocument = GetTemplateXML();
MemoryStream memStm = null, tempStream = null;
try
{
memStm = tempStream = new MemoryStream();
using (StreamWriter sw = new StreamWriter(memStm, Encoding.UTF8))
{
// Serialize object into raw xml
memStm = null;
serializer.Serialize(sw, invoiceToSerialize);
sw.Flush();
// parse raw xml into Xml document
tempStream.Position = 0;
settings.IgnoreWhitespace = true;
var xtr = XmlReader.Create(tempStream, settings);
toReturn.Load(xtr);
}
}
finally
{
if (memStm != null)
{
memStm.Dispose();
}
}
return toReturn;
}
public static T DeserializeXML<T>(string responseString)
where T : class
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(responseString))
{
return (T)xmlSerializer.Deserialize(sr);
}
}
public XmlDocument GetTemplateXML()
{
XmlDocument toReturn = new XmlDocument();
var decl = toReturn.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
toReturn.AppendChild(decl);
return toReturn;
}
private XmlDocument GenerateXmlResponse(XmlDocument innerXMLDocument)
{
XmlDocument toReturn = GetTemplateXML();
XmlElement requestElement = toReturn.CreateElement("response");
requestElement.SetAttribute("status", "success");
requestElement.InnerXml = innerXMLDocument.DocumentElement.OuterXml;
toReturn.AppendChild(requestElement);
return toReturn;
}
/// <summary>
/// Web response from creating a invoice
/// </summary>
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", ElementName = "response")]
public class GetInvoiceResponse
{
/// <summary>
/// Gets or sets the response status
/// </summary>
/// <value>
/// reponse Status
/// </value>
[XmlAttribute("status")]
public string ResponseStatus { get; set; }
/// <summary>
/// Gets or sets the new invoice id
/// </summary>
/// <value>
/// generated by invoicera for this response
/// </value>
[XmlElement(ElementName = "invoice")]
public Invoice Invoice { get; set; }
}
}

PaymentDetails is not successfully deserialized, returning a null object, because it seems it is expected to have IXmlDeserializable

[XmlRoot("Quote")]
public class Quote
{
[XmlElement("Insurance")]
public InsuranceDetails InsDetails { get; set; }
[XmlElement("Payment")]
public PaymentDetails PayDetails { get; set; }
}
public class InsuranceDetails : IXmlSerializable
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("Insurance");
Details1 = reader.ReadElementString("Details1");
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
// do write suff
}
}
public class PaymentDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
Given this example, using XmlSerializer to deserialize my string to QuoteObject, PaymentDetails is not successfully deserialized, returning a null object, because it seems it is expected to have IXmlDeserializable. It only works if PaymentDetails is parsed in first place. Is this some expected behavior from XmlSerializer?
using (TextReader read = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(Quote));
return (Quote)serializer.Deserialize(read);
}
Well these are the ReadXml and WriteXml I modified:
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
var empty=reader.IsEmptyElement;
reader.ReadStartElement();
if(!empty){
Details1=reader.ReadElementString("Details1");
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
var str=string.IsNullOrWhiteSpace(Details1)?"":Details1;
writer.WriteElementString("Details1",str);
}
Following are serialize and deserialize functions:
public static string Serialize<T>(T t)
{
var xmlser=new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
using(StringWriter textWriter = new StringWriter()) {
using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
xmlser.Serialize(xmlWriter, t);
}
return textWriter.ToString();
}
}
public static T Deserialize<T>(string xml)
{
if(string.IsNullOrEmpty(xml)) {
return default(T);
}
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlReaderSettings settings = new XmlReaderSettings();
using(StringReader textReader = new StringReader(xml)) {
using(XmlReader xmlReader = XmlReader.Create(textReader, settings)) {
return (T) serializer.Deserialize(xmlReader);
}
}
}
Serialization Test:
var q=new Quote();
q.PayDetails = new PaymentDetails{Details1="Payment Details 1"};
q.InsDetails=new InsuranceDetails{Details1="Insurance Details 1"};
str = Serialize<Quote>(q);
Which gives (str):
<?xml version="1.0" encoding="utf-16"?>
<Quote xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Payment>
<Details1>Payment Details 1</Details1>
</Payment>
<Insurance>
<Details1>Insurance Details 1</Details1>
</Insurance>
</Quote>
Deserialization Test:
var dq=Deserialize<Quote>(str);
Console.WriteLine(dq.PaymentDetails.Detail1);//gives "Payment Details 1"
Console.WriteLine(dq.InsuranceDetails.Detail1);//gives "Insurance Details 1"
PS:- The Serialize code was copied from another SO answer verbatim. I learned how to serialize to string using StringWriter.
First of all you don't have to implement IXmlSerializable in any of the classes. Second of all, you don't provide the content of the xml variable. It may contain a mistype/bug, if you created it manually.
I used the following code, to test your classes:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace XmlDeSerialize
{
[XmlRoot("Quote")]
public class Quote
{
[XmlElement("Insurance")]
public InsuranceDetails InsDetails { get; set; }
[XmlElement("Payment")]
public PaymentDetails PayDetails { get; set; }
}
public class InsuranceDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
public class PaymentDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var qin = new Quote
{
InsDetails = new InsuranceDetails { Details1 = "insurance details text" },
PayDetails = new PaymentDetails { Details1 = "payment details text" },
};
string xml;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(Quote));
serializer.Serialize(stream, qin);
stream.Position = 0;
using (var sr = new StreamReader(stream))
{
xml = sr.ReadToEnd();
}
}
Quote qout;
using (TextReader read = new StringReader(xml))
{
var deserializer = new XmlSerializer(typeof(Quote));
var obj = deserializer.Deserialize(read);
qout = (Quote)obj;
}
Console.WriteLine("InsDetails.Details1='{0}'", qout.InsDetails.Details1);
Console.WriteLine("PayDetails.Details1='{0}'", qout.PayDetails.Details1);
}
}
}
The value of xml after serialization:
<?xml version="1.0"?>
<Quote xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Insurance>
<Details1>insurance details text</Details1>
</Insurance>
<Payment>
<Details1>payment details text</Details1>
</Payment>
</Quote>
The console output I received:
InsDetails.Details1='insurance details text'
PayDetails.Details1='payment details text'
Try the code yourself and see if it works for you. Clearly to me you don't provide valid XML content for deserialization, or other part of your code you did not provide in your question is to blame.

serialize and store object in another object that implements IXmlSerializable

I would like to XML serialize instances of my object Exception and store it in the XMLNode[] Nodes property of another object ExceptionReport.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Xml.Serialization.XmlSchemaProviderAttribute("ExportSchema")]
[System.Xml.Serialization.XmlRootAttribute(IsNullable = false)]
public partial class ExceptionReport : object, System.Xml.Serialization.IXmlSerializable
{
public System.Xml.XmlNode[] Nodes { get; set; }
public void ReadXml(System.Xml.XmlReader reader)
{
this.Nodes = System.Runtime.Serialization.XmlSerializableServices.ReadNodes(reader);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
System.Runtime.Serialization.XmlSerializableServices.WriteNodes(writer, this.Nodes);
}
}
public class Exception
{
public string ExceptionText;
public string exceptionCode;
public string locator;
}
How would i go about doing this so the result would be something like this:
<ExceptionReport xmlns="http://www.opengis.net/ows" >
<Exception exceptionCode="1">my first instance</Exception>
<Exception exceptionCode="2">my second instance</Exception>
</ExceptionReport>
So far i have the following but i need to know how to serialize these objects and store them in the ExceptionReport Nodes array.
ExceptionReport er = new ExceptionReport();
Exception exception_item1 = new Exception();
exception_item1.ExceptionText = "my first instance";
exception_item1.exceptionCode = "1";
Exception exception_item2 = new Exception();
exception_item2.ExceptionText = "my second instance";
exception_item2.exceptionCode = "2";
List<Exception> exceptions = new List<Exception>( exception_item1, exception_item2 );
[XmlRoot("ExceptionReport")]
public partial class ExceptionReport
{
[XmlElement("Exception")]
public List<Exception> Nodes { get; set; }
public ExceptionReport()
{
Nodes = new List<Exception>();
}
}
public class Exception
{
[XmlText]
public string ExceptionText;
[XmlAttribute("exceptionCode")]
public int ExceptionCode;
[XmlAttribute("locator")]
public string Locator;
}
Then to serialize, I use the following extensions:
public static bool XmlSerialize<T>(this T item, string fileName)
{
return item.XmlSerialize(fileName, true);
}
public static bool XmlSerialize<T>(this T item, string fileName, bool removeNamespaces)
{
object locker = new object();
XmlSerializerNamespaces xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
lock (locker)
{
using (XmlWriter writer = XmlWriter.Create(fileName, settings))
{
if (removeNamespaces)
{
xmlSerializer.Serialize(writer, item, xmlns);
}
else { xmlSerializer.Serialize(writer, item); }
writer.Close();
}
}
return true;
}
public static T XmlDeserialize<T>(this string s)
{
object locker = new object();
StringReader stringReader = new StringReader(s);
XmlTextReader reader = new XmlTextReader(stringReader);
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
lock (locker)
{
T item = (T)xmlSerializer.Deserialize(reader);
reader.Close();
return item;
}
}
finally
{
if (reader != null)
{ reader.Close(); }
}
}
public static T XmlDeserialize<T>(this FileInfo fileInfo)
{
string xml = string.Empty;
using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
return sr.ReadToEnd().XmlDeserialize<T>();
}
}
}
Use like this:
ExceptionReport report = new ExceptionReport();
report.Nodes.Add(new Exception { ExceptionText = "my first instance", ExceptionCode = 1, Locator = "loc1" });
report.Nodes.Add(new Exception { ExceptionText = "my second instance", ExceptionCode = 2 });
report.XmlSerialize("C:\\test.xml");
I tested and it came out like you wanted. Hope it helps...
PS - The extensions came from my library on codeproject: http://www.codeproject.com/KB/dotnet/MBGExtensionsLibrary.aspx

How to create xml file from a list of objects

I defined 3 classes:
public class PublishedPage
{
public string Action { get; private set; }
public string PageGuid { get; set; }
public List<string> SearchableProperties { get; set; }
public PublishedPage()
{
Action = "Published";
SearchableProperties = new List<string>();
}
}
public class DeletedPage
{
public string Action { get; private set; }
public string PageGuid { get; set; }
public DeletedPage()
{
Action = "Deleted";
}
}
public class MovedPage
{
public string Action { get; private set; }
public string PageGuid { get; set; }
public string OldParentGuid { get; set; }
public string NewParentGuid { get; set; }
public MovedPage()
{
Action = "Moved";
}
}
Somewhere in code I have something like this:
List<PublishedPage> publishedPages = GetPublishedPages();
List<MovedPage> movedPages = GetMovedPages();
List<DeletedPage> deletedPages = GetDeletedPages();
Now I want to create a XML file containing these 3 collections but don't know how.
XML should be like this:
<PublishedPages>
<PublishedPage>
<Action>Published</Action>
<PageGuid>.....</PageGuid>
<SearchableProperties>
<Name>Name 1</Name>
<Name>Name 2</Name>
</SearchablePeoperties>
</PublishedPage>
<PublishedPage>
...
<PublishedPage>
</PublishedPages>
<MovedPages>
...
</MovedPages>
<DeletedPages>
...
</DeletedPages>
Any help would be appreciated.
Thank you!
Even though the XmlSerializer is the easiest one, if you already know the schema you can do it with a bit of linq to xml too:
XElement element =
new XElement("PublishedPages",
(from page in publishedPages
select new XElement("PublishedPage",
new XElement("Action", page.Action),
new XElement("PageGuid",page.PageGuid),
new XElement("SearchableProperties",
(from property in page.SearchableProperties
select new XElement("Name",property)))
)
)
);
Serialization is fairly slow. performance -wise. A similar approach would be something like this:
StringWriter stringWriter = new StringWriter();
XmlTextWriter xmltextWriter = new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
// Start document
xmltextWriter.WriteStartDocument();
xmltextWriter.WriteStartElement("ROOT");
foreach (PublishedPage page in publishedPages)
{
//Create a page element
xmltextWriter.WriteStartElement("Page");
xmltextWriter.WriteAttributeString("Action", page.Action);
xmltextWriter.WriteAttributeString("SearchableProperties", page.SearchableProperties);
xmltextWriter.WriteEndElement();
}
// Same for the other lists
// End document
xmltextWriter.WriteEndElement();
xmltextWriter.Flush();
xmltextWriter.Close();
stringWriter.Flush();
Use serialization, a good example is here: http://support.microsoft.com/kb/815813
What you need is XML Serialization as indicated by #Kyndings. But I will give you some code snippets to help:
In order to serialize one object your code should be something similar to
string serializedPublishedPage = Serializer.SerializeObject(PublishedPage, typeof(PublishedPage));
To have all three in the same XML you create a function that generates a list of XMLElements :
private List<XmlElement> functionA ()
{
XmlDocument doc = new XmlDocument();
List<XmlElement> elementList = new List<XmlElement>();
XmlElement element;
string serializedPublishedPage = Serializer.SerializeObject(PublishedPage, typeof(PublishedPage));
string serializedDeletedPage = Serializer.SerializeObject(DeletedPage, typeof(DeletedPage));
string serializedMovedPage = Serializer.SerializeObject(MovedPage, typeof(MovedPage));
doc.LoadXml(serializedDemographic);
element = doc.DocumentElement;
elementList.Add(element);
return elementList;
}
And later use it:
XmlDocument xmlData = new XmlDocument();
XmlElement root = xmlData.CreateElement("WHATEVER");
XmlElement Element;
XmlNode Node;
XmlElement AuxElement;
XmlNode AuxNode;
foreach (XmlElement xmlElement in functionA())
{
AuxNode= doc.ImportNode(xmlElement, true);
AuxElement.AppendChild(node);
}
// Now you have your XML objects in AuxElement
Node = xmlData.ImportNode(AuxElement, true);
root.AppendChild(Node);
// you have your full XML in xmlData in xmlData.InnerXml
Try this, I'm using Xml Serialization :
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Pages pages = new Pages();
pages.PublishedPages.Add(
new PublishedPage()
{
PageGuid = Guid.NewGuid().ToString(),
SearchableProperties = new List<string>()
{
"Foo",
"Bar",
}
});
pages.PublishedPages.Add(
new PublishedPage()
{
PageGuid = Guid.NewGuid().ToString(),
SearchableProperties = new List<string>()
{
"Tic",
"Tac",
"Toe",
}
});
pages.DeletedPages.Add(
new DeletedPage()
{
PageGuid = Guid.NewGuid().ToString(),
});
pages.DeletedPages.Add(
new DeletedPage()
{
PageGuid = Guid.NewGuid().ToString(),
});
pages.MovedPages.Add(
new MovedPage()
{
PageGuid = Guid.NewGuid().ToString(),
OldParentGuid = Guid.NewGuid().ToString(),
NewParentGuid = Guid.NewGuid().ToString(),
});
pages.MovedPages.Add(
new MovedPage()
{
PageGuid = Guid.NewGuid().ToString(),
OldParentGuid = Guid.NewGuid().ToString(),
NewParentGuid = Guid.NewGuid().ToString(),
});
Console.WriteLine(SerializeObject(pages));
}
/// <summary>
/// To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String.
/// </summary>
/// <param name="characters">Unicode Byte Array to be converted to String</param>
/// <returns>String converted from Unicode Byte Array</returns>
private static string UTF8ByteArrayToString(Byte[] characters)
{
UTF8Encoding encoding = new UTF8Encoding();
string constructedString = encoding.GetString(characters);
return (constructedString);
}
/// <summary>
/// Converts the String to UTF8 Byte array and is used in De serialization
/// </summary>
/// <param name="pXmlString"></param>
/// <returns></returns>
private static Byte[] StringToUTF8ByteArray(string pXmlString)
{
UTF8Encoding encoding = new UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(pXmlString);
return byteArray;
}
/// <summary>
/// From http://www.dotnetjohn.com/articles.aspx?articleid=173
/// Method to convert a custom Object to XML string
/// </summary>
/// <param name="pObject">Object that is to be serialized to XML</param>
/// <returns>XML string</returns>
public static string SerializeObject(Object pObject)
{
try
{
string xmlizedString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(pObject.GetType());
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
xmlTextWriter.Formatting = Formatting.Indented;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
xs.Serialize(xmlTextWriter, pObject, ns);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
xmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
return xmlizedString;
}
catch (Exception e)
{
System.Console.WriteLine(e);
return null;
}
}
}
[XmlRoot(ElementName="Pages", Namespace="")]
public class Pages
{
public List<PublishedPage> PublishedPages { get; set; }
public List<MovedPage> MovedPages { get; set; }
public List<DeletedPage> DeletedPages { get; set; }
public Pages()
{
PublishedPages = new List<PublishedPage>();
MovedPages = new List<MovedPage>();
DeletedPages = new List<DeletedPage>();
}
}
public class PublishedPage
{
public string Action { get; set; }
public string PageGuid { get; set; }
public List<string> SearchableProperties { get; set; }
public PublishedPage()
{
Action = "Published";
SearchableProperties = new List<string>();
}
}
public class DeletedPage
{
public string Action { get; set; }
public string PageGuid { get; set; }
public DeletedPage()
{
Action = "Deleted";
}
}
public class MovedPage
{
public string Action { get; set; }
public string PageGuid { get; set; }
public string OldParentGuid { get; set; }
public string NewParentGuid { get; set; }
public MovedPage()
{
Action = "Moved";
}
}
}

Categories