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.
Related
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!
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; }
}
}
I have application, which must contain map. To realize this task I create some classes:
1. Map (contains Image and two objects of Location class)
2. GPSPoint (contains two objects of ICoordinate)
3. ImagePoint (contains two int variables)
4. Location (contains GPSPoint and ImagePoint)
And one interface, and two classes, which realize it:
1. ICoordinate
2. GpsCoordinateDeg
3. GpsCoordinateDegMinSec
All of them implements ISerialization interface and have public void GetObjectData(SerializationInfo, StreamingContext) methods.
I want to save my map in the file, and I realize one of the methods of serialization, but it isn't work - I get void xml file:
<?xml version="1.0"?>
<Map xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
I use my class for serialization in this code:
[Serializable]
class Map : ISerializable {
...
public void saveInFile(string filepath) {
Serializer serializer = new Serializer();
serializer.SerializeObject(this, filepath);
}
...
}
This is code of my Serializer:
class Serializer {
public void SerializeObject<T>(T serializableObject, string fileName) {
if (serializableObject == null) { return; }
try {
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream()) {
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
}
} catch (Exception ex) {
//Log exception here
}
}
public T DeSerializeObject<T>(string fileName) {
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try {
string attributeXml = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString)) {
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read)) {
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
} catch (Exception ex) {
//Log exception here
}
return objectOut;
}
}
Where is the problem?
As i dont know the complete implementation of your poco classes i suggest you to look further to your GPSPoint class:
GPSPoint (contains two objects of ICoordinate)
You can not serialize an interface. The problem is that an interface is an opaque type. There is no way for the serializer to know what to write out and more importantly what to create when it needs to serialize things back.
You may look into StackOverflow for "serializing interfaces"-postings. I hope it helps.
Bad idea going for generics with serialization since it heavily relies on object boxing and unboxing.
My way of doing this does not rely on implementing ISerializable, rather on the careful use of attributes.
Decorate each class below with [Serializable()], [XmlType(AnonymousType=true)], [XmlRoot(Namespace="", IsNullable=false)]. The enum stands for coordinate types. Derive your existing classes from these ones. Mark properties you do not want serialized with [XmlIgnore] in your derived classes. Make Coordinate below implement your ICoordinate.
public partial class Location {
[XmlElement]
public GPSPoint GPSPoint { get; set; }
[XmlElement]
public ImagePoint ImagePoint { get; set; }
}
public partial class GPSPoint {
[XmlElement(ElementName = "Coordinate", Order = 0)]
public Coordinate Coordinate1 {get; set; }
[XmlElement(Order=1, ElementName="Coordinate")]
public Coordinate Coordinate2 {get;set;}
}
public partial class Coordinate {
[XmlAttribute()]
public ICoordinateType type {get;set;}
}
[Serializable]
public enum ICoordinateType {
/// <remarks/>
GpsCoordinateDegMinSec,
/// <remarks/>
GpsCoordinateDeg,
}
public partial class Map {
[XmlElement(Order=0, IsNullable=true)]
public object Image { get; set; }
/// <remarks/>
[XmlElement(ElementName="Location", Order = 1)]
public Location Location1 {get; set; }
/// <remarks/>
[XmlElement(ElementName = "Location", Order = 2)]
public Location Location2 {get; set; }
}
This is tested and running:
var tc1 = new xyz.Coordinate () {type = xyz.ICoordinateType.GpsCoordinateDeg};
var tc2 = new xyz.Coordinate () {type = xyz.ICoordinateType.GpsCoordinateDegMinSec};
var l1 = new xyz.Location() {
GPSPoint = new xyz.GPSPoint() { Coordinate1 = tc1, Coordinate2 = tc2 },
ImagePoint = new xyz.ImagePoint() { x = 0, y = 0 } };
xyz.Map m = new xyz.Map() {
Image = null,
Location1 = l1,
Location2 = new xyz.Location() {
GPSPoint = new xyz.GPSPoint() {
Coordinate1 = tc1, Coordinate2 = tc2 },
ImagePoint = new xyz.ImagePoint() { x = 1, y = 2 }
} };
XmlSerializer xs = new XmlSerializer(typeof(Map));
using (var fs = File.Create("map.xml") ) { xs.Serialize(fs, m); }
[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.
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";
}
}
}