I'm trying to deserialize some xml into a C# object. The trick is for the most part, I know what this object will look like. However, this is one child that has dynamic elements.
(here is an example)
<measurement>
<Time>2021-02-02</Time>
<ID>1</ID>
<LeftWheel>
<ValuesRead>
<DynamicValue>12.3</DynamicValue>
<DynamicValue2>2.3</DynamicValue2>
<DynamicValue4>1.3</DynamicValue4>
<DynamicValue3>10.3</DynamicValue3>
</ValuesRead>
</LeftWheel>
<RightWheel>
<ValuesRead>
<DynamicValue>12.3</DynamicValue>
<DynamicValue2>2.3</DynamicValue2>
<DynamicValue6>1.3</DynamicValue6>
<DynamicValue10>10.3</DynamicValue10>
</ValuesRead>
</RightWheel>
</measurement>
In this XML, Measurement, Time, and ID are always going to in the object.
The LeftWheel and RightWheel elements are always going to be there with ValuesRead, but the ValuesRead children are dynamic and can be anything.
I have tried making a C# object to reflect most the structure, and then using the XmlSerializer.UnknownElement to pick up the unknown elements in the ValuesRead element, but I cannot link it to the parent above to know if it is on the LeftWheel or RightWheel.
XmlSerializer serializer = new XmlSerializer(typeof(FVISSiteEvent));
serializer.UnknownElement += UnknownElementFound;
Is there a way I can define the LeftWheel and RightWheel classes to be dynamic for the serialization, while having the other classes not dynamic?
You should be able to use the UnknownElementFound event to manually handle these aspects of serialization. See: Serialize XML array of unknown element name
Other options could be to specify the types you expect to see as XmlElementAtrribute decorated properties and they will just be null if they aren’t deserialized.
There’s also the nuclear option of implementing IXmlSerializable in your class and taking full control of the deserialization.
Uses Custom Serializer :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Linq;
using System.Xml.Schema;
namespace ConsoleApplication16
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(INPUT_FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Measurement));
Measurement measurement = (Measurement)serializer.Deserialize(reader);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME,settings);
serializer.Serialize(writer, measurement);
}
}
[XmlRoot("measurement")]
public class Measurement
{
public DateTime Time { get; set; }
public int ID { get; set; }
[XmlArray("LeftWheel")]
[XmlArrayItem("ValuesRead")]
public List<Wheel> leftWheel { get; set; }
[XmlArray("RightWheel")]
[XmlArrayItem("ValuesRead")]
public List<Wheel> rightWheel { get; set; }
}
public class Wheel : IXmlSerializable
{
List<decimal> values { get; set; }
// Xml Serialization Infrastructure
public void WriteXml(XmlWriter writer)
{
int count = 0;
XElement valuesRead = new XElement("ValuesRead");
for (int i = 0; i < values.Count; i++ )
{
valuesRead.Add(new XElement("ValuesRead" + (i == 0? "" : i.ToString()), values[i]));
}
writer.WriteRaw(valuesRead.ToString());
}
public void ReadXml(XmlReader reader)
{
XElement values = (XElement)XElement.ReadFrom(reader);
this.values = values.Elements().Where(x => x.Name.LocalName.StartsWith("DynamicValue")).Select(x => (decimal)x).ToList();
}
public XmlSchema GetSchema()
{
return (null);
}
}
}
Besides using custom Xml serialization to deserialize your xml file, here is one another approach using Cinchoo ETL - an open source library to handle it simple way (those open to try it!)
Define POCO Class
public class Measurement
{
public DateTime Time { get; set; }
public int ID { get; set; }
[ChoXPath("LeftWheel/ValuesRead/*")]
public double[] LeftWheel { get; set; }
[ChoXPath("RightWheel/ValuesRead")]
public dynamic RightWheel { get; set; }
}
Deserialize using ChoETL
using (var r = ChoXmlReader<Measurement>.LoadText(xml)
.WithXPath("/")
)
{
foreach (var rec in r)
rec.Print();
}
Sample fiddle: https://dotnetfiddle.net/KtNvra
Disclaimer: I'm author of this library.
I've managed to resolve this using a dynamic type when deserializing.
When I deserialize ValuesRead, it is a defined as a dynamic type.
When deserialized, it turns into an XmlNode and from there I iterate over the node use the Name and InnerText values to read all the data.
Related
I want to reduce the clutter in serialization output, introducing default values for often-the-same or not-used properties.
But still, they are in the output. What am I doing wrong?
This should be complete (though not compile):
[Serializable]
public class MyClass
{
[DefaultValue(null)]
public string Alias { get; set; } = null;
[DefaultValue(false)]
public bool Deactivated { get; set; } = false;
[DefaultValue(null)]
public bool? MyNullable { get; set; } = null;
}
public static string SerializeFromObject<T>(this T toSerialize)
{
var xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
var myClass = new MyClass();
var str = SerializeFromObject(myClass);
And here the xml output, still including the nullable:
<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MyNullable xsi:nil="true" />
</MyClass>
How to get rid of the nullable in the serialized xml?
Self's answer was pretty helpful, but in the end there were some issues so I didn't follow it. I will outline it here later on for posterity though so it doesn't get lost in the comments or through a link going offline.
My own solution:
use standard .net xml serialization, re-read serialized string into XElement, remove all "nil"s. And then .ToString() it again.
Definitely a medium-nice solution, so feel free to come up with something nicer.
Conditional serialization as suggested would mean too much additional code for me which I would like to avoid.
My solution also has the disadvantage that DefaultValues cannot be specified for the nullables, they are always omitted when being null. Which is fine for me though. I use a nullable when I have no default value.
/// <summary>
/// use for compact serializations
/// nullables that don't have a value are omitted (irrespecitve of DefaultValue!)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="toSerialize"></param>
/// <returns></returns>
public static string SerializeFromObject_NoNils<T>(T toSerialize)
{
var ele = Serialize<T>(toSerialize);
void removeNils(XNode node)
{
// recursion
if (node is XElement elem)
{
foreach (var child in elem.DescendantNodes())
removeNils(child);
//foreach (var child in elem.Descendants())
// removeNils(child);
}
// same level
while (node != null)
{
var nextnode = node.NextNode;
//if (node.)
if ((node as System.Xml.Linq.XElement)?.Attribute("{http://www.w3.org/2001/XMLSchema-instance}nil")?.Value == "true")
node.Remove();
node = nextnode;
}
}
removeNils(ele.FirstNode);
return ele.ToString();
}
If someone want to build on or improve Self's answer - which has the disadvantage, that the DefaultValue attribute seems not to work (it seems to work on default(type) rather than that attribute), here it is copy/pasted from his link, with an added empty namespace because the default .net deserialization stumbles across the DataSerializerContract namespace.
So, this is not my code, credit goes to user Self.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Runtime.Serialization;
public class Program
{
public static void Main()
{
var myClass = new MyClass();
var str_dc = DataContract_SerializeFromObject(myClass);
str_dc.Dump();
var str_xml = SerializeFromObject(myClass);
str_xml.Dump();
}
public static string SerializeFromObject<T>( T toSerialize)
{
var xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
public static string DataContract_SerializeFromObject<T>( T toSerialize)
{
var xmlSerializer = new DataContractSerializer(toSerialize.GetType());
using (var output = new StringWriter())
using (var writer = new XmlTextWriter(output) { Formatting = Formatting.Indented })
{
xmlSerializer.WriteObject(writer, toSerialize);
return output.GetStringBuilder().ToString();
}
}
}
[DataContract(Namespace = "")] // default namespace is not deserializable with standard functionality
[Serializable]
public class MyClass
{
[DataMember(EmitDefaultValue = false)] [DefaultValue(null)]
public string Alias { get; set; } = null;
[DataMember(EmitDefaultValue = false)] [DefaultValue(false)]
public bool Deactivated { get; set; } = false;
[DataMember(EmitDefaultValue = false)] [DefaultValue(null)]
public bool? MyNullable { get; set; } = null;
}
I have an issue when deserializing an object (which I cannot modify).
I receive an exponential value for certain xml element and for represent them in my class I used decimal value expect that when I deserialize the xml document it fails.
<fwdRate>-9.72316862724032E-05</fwdRate>
Is there any solution to represent this attribute other than create 2 attributes in my class to represent it (one string and the other a decimal value)?
Can I create a custom deserializtion class for decimal value?
private void ParseXML(string value)
{
XmlSerializer serializer = new XmlSerializer(typeof(SwapDataSynapseResult));
using (TextReader reader = new StringReader(value))
{
_result = serializer.Deserialize(reader) as SwapDataSynapseResult;
}
}
As Demand
using System;
using System.IO;
using System.Xml.Serialization;
[XmlRoot(ElementName = "result")]
public class Result
{
[XmlElement(ElementName = "fwdRate")]
public decimal FwdRate { get; set; }
}
public class Program
{
public static void Main()
{
string val = "<result><fwdRate>-9.72316862724032E-05</fwdRate></result>";
Result response = ParseXML(val);
}
static Result ParseXML(string value)
{
XmlSerializer serializer = new XmlSerializer(typeof(Result));
using (TextReader reader = new StringReader(value))
{
return serializer.Deserialize(reader) as Result;
}
}
}
In XML, decimal values are not allowed to use scientific (exponential) notation (See this link at the 'Restrictions' paragraph).
Either:
the value is indeed a floating point one: Put a float/double instead of a decimal in the code.
the XML is corrupted.
In the same way, in C#, by default, Decimal.Parse doesn't accept exponential representation.
You can override this behavior by implementing a new struct that wrap a decimal and implement IXmlSerializable and allow exponential representation when de-serialized:
public struct XmlDecimal : IXmlSerializable
{
public decimal Value { get; private set; }
public XmlDecimal(decimal value) => Value = value;
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader)
{
var s = reader.ReadElementContentAsString();
Value = decimal.TryParse(s, NumberStyles.Number | NumberStyles.AllowExponent,
NumberFormatInfo.InvariantInfo, out var value)
? value
: 0; // If parse fail the resulting value is 0. Maybe we can throw an exception here.
}
public void WriteXml(XmlWriter writer) => writer.WriteValue(Value);
public static implicit operator decimal(XmlDecimal v) => v.Value;
public override string ToString() => Value.ToString();
}
The flaw is that you have to use this struct instead of a decimal everywhere in your model.
And sadly you can't make this struct read-only, has explained here.
The proper way is to control how your properties are deserialized by implementing the IXmlSerializable interface:
IXmlSerializable
IXmlSerializable code project example
In the ReadXml method you should convert your number
var yourStringNumber = ...
this.fwdRate = Decimal.Parse(yourStringNumber, System.Globalization.NumberStyles.Float);
But this method will require you to parse of the whole xml manually that is a bit overhead sometimes.
A simple solution (that smells but might be useful) is just to add additional fwdRateDecimal field to your class and fulfill the value after the serialization.
private void ParseXML(string value)
{
XmlSerializer serializer = new XmlSerializer(typeof(SwapDataSynapseResult));
using (TextReader reader = new StringReader(value))
{
_result = serializer.Deserialize(reader) as SwapDataSynapseResult;
_result.fwdRateDecimal = Decimal.Parse(_result.fwdRate, System.Globalization.NumberStyles.Float)
}
}
Also conversion can be implemented in a type directly:
[XmlRoot(ElementName = "result")]
public class Result
{
[XmlElement(ElementName = "fwdRate")]
public string FwdRateStr { get; set; }
private string lastParsedValue = null;
private decimal fwdRate = 0;
[XmlIgnore]
public decimal FwdRate
{
get
{
if(FwdRateStr != lastParsedValue)
{
lastParsedValue = FwdRateStr
fwdRate = Decimal.Parse(FwdRateStr ,System.Globalization.NumberStyles.Float)
}
return fwdRate
}
}
This is the solution:
using System;
using System.IO;
using System.Xml.Serialization;
[XmlRoot(ElementName = "result")]
public class Result
{
[XmlElement(ElementName = "fwdRate")]
public double FwdRate { get; set; }
}
public class Program
{
public static void Main()
{
string val = "<result><fwdRate>-9.72316862724032E-05</fwdRate></result>";
Result response = ParseXML(val);
Console.WriteLine(response.FwdRate);
}
static Result ParseXML(string value)
{
XmlSerializer serializer = new XmlSerializer(typeof(Result));
using (TextReader reader = new StringReader(value))
{
return serializer.Deserialize(reader) as Result;
}
}
}
You can't modify the incoming xml but you can modify how you read its' data.
Read that number as a double instead of decimal and you will have the right decimal precision.
Output:
-9,72316862724032E-05
I'm working with some third party XML that has no formally defined schema, only example XML. I have several thousand XML files from this third party. There is no guarantee that every possible element lies within one or more of these files. The third party service could send me a new file with a new element!
I can view these files and reverse engineer types relatively easily.
For example:
<MyObject>
<MyProperty>Some value</MyProperty>
</MyObject>
Could deserialize to
public class MyObject
{
public string MyProperty { get; set; }
}
No problems so far.
But what if I attempt to deserialize this:
<MyObject>
<MyProperty>Some value</MyProperty>
<MyOtherProperty>Some value</MyOtherProperty>
</MyObject>
into my class above? I want it to throw an exception, so I can be notified that my class does not accommodate MyOtherProperty.
Is there a way to do this?
I'd like to share the code I wrote using the accepted answer. Using the below utility method, I can deserialize without checks for unknown stuff, and with checks, by setting strict=true.
I hope readers find this useful!
public static T XmlDeserialize<T>(string xml, bool strict = false)
{
using (var stringReader = new StringReader(xml))
{
using (var xmlTextReader = new XmlTextReader(stringReader))
{
var xmlSerializer = new XmlSerializer(typeof(T));
if (strict)
{
var options = new XmlDeserializationEvents();
options.OnUnknownElement += (sender, args) =>
{
throw new XmlDeserializationException(xml, $"Unexpected Element {args.Element.LocalName} on line {args.LineNumber}.");
};
options.OnUnknownAttribute += (sender, args) =>
{
throw new XmlDeserializationException(xml, $"Unexpected Element: {args.Attr.LocalName} on line {args.LineNumber}.");
};
options.OnUnknownNode += (sender, args) =>
{
throw new XmlDeserializationException(xml, $"Unexpected Element: {args.LocalName} on line {args.LineNumber}.");
};
return (T)xmlSerializer.Deserialize(xmlTextReader, options);
}
return (T)xmlSerializer.Deserialize(xmlTextReader);
}
}
}
And that exception class I'm throwing looks like this:
public class XmlDeserializationException : Exception
{
public string Xml { get; private set; }
public XmlDeserializationException(
string xml, string message) : base (message)
{
Xml = xml;
}
}
I can check my logs and look up the line number in the actual xml. Works perfectly. Thanks, pfx.
The XmlSerializer has a Deserialize overload allowing to pass in an options element by which to hook to some events; eg. OnUnknownElement.
XmlDeserializationEvents options = new XmlDeserializationEvents();
options.OnUnknownElement += (sender, args) => {
XmlElement unknownElement = args.Element;
// throw an Exception with this info.
} ;
var o = serializer.Deserialize(xml, options) as MyObject;
The OnUnknowElement takes nested elements into account.
With the classes below
public class MyObject
{
public string MyProperty { get; set; }
public MyOtherObject Other { get; set; }
}
public class MyOtherObject
{
public string SomeProperty { get; set; }
}
and the following xml
<MyObject>
<MyProperty>Some value</MyProperty>
<Other>
<SomeProperty>...</SomeProperty>
<UFO>...</UFO>
</Other>
</MyObject>
The OnUnknowElement handler will trigger for the UFO element.
One way of doing so would be to use your current object model and create an XSD out of it. You can then check new files against that XSD and throw if it doesn't validate.
Extend your class with an XmlAnyElement container.
Any unknown elements will end up in that array.
After deserialization check whether that array is empty.
public class MyObject
{
[XmlAnyElement]
public XmlElement[] UnknownElements;
public string MyProperty { get; set; }
}
I am working with the USPS Tracking API. The have a specification for a request as I have listed below;
<TrackFieldRequest PASSWORD="" USERID="prodsolclient" APPID="">
<Revision>1</Revision>
<ClientIp>111.0.0.1</ClientIp>
<TrackID ID="5551212699300000962610" />
</TrackFieldRequest>
And they state in their user manual that "Up to 10 tracking IDs may be contained in each request input to the Web Tool server."
I interpret this as meaning that the TrackFieldRequest can have up to 10 of the TrackID child elements. However, these multiple TrackID elements are not defined as being in an array. They are just up to 10 consecutive TrackID child elements of the TrackFieldRequest element.
So, I am not sure how to build up the CLR object to pass to the XMLSerializer if I want to include 10 of the TrackID child elements.
I tried creating a TrackFieldRequest class that has a property that is a "List TrackIds" but the USPS website gives me an error response saying "The element 'TrackFieldRequest' has invalid child element 'TrackIds'. List of possible elements expected: 'TrackID'"
How do I model the CLR class so that the XMLSerializer can use it to generate up to 10 TrackID child elements, without using a List or Array property in my TrackFieldRequest class?
Here is my current TrackFieldRequest class
public class TrackFieldRequest
{
// Based upon USPS Web Tools API User Guide(Track & Confirm API) version 3.3 dated 2/28/16
// at https://www.usps.com/business/web-tools-apis/track-and-confirm-api.pdf
[XmlAttribute("USERID")]
public string UserId { get; set; }
[XmlElement("Revision")]
public int Revision { get; set; }
[XmlElement("ClientIp")]
public string ClientIp { get; set; }
[XmlElement("SourceIdZIP")]
public string SourceIdZip { get; set; }
public List<TrackId> TrackIds { get; set; }
}
Here is my current TrackID class
public class TrackId
{
// Based upon USPS Web Tools API User Guide(Track & Confirm API) version 3.3 dated 2/28/16
// at https://www.usps.com/business/web-tools-apis/track-and-confirm-api.pdf
public TrackId(string a_Id, string a_destinationZipCode, string a_mailingDate)
{
ID = a_Id;
DestinationZipCode = a_destinationZipCode;
MailingDate = a_mailingDate.ToString();
}
// Parameterless constructor is needed for the XMLSerializer
public TrackId()
{
}
[XmlAttribute]
public string ID { get; set; }
[XmlElement("DestinationZipCode")]
public string DestinationZipCode { get; set; }
[XmlElement("MailingDate")]
public string MailingDate { get; set; }
}
Here is my methods to convert the the CLR class into Xml using an XmlWriter
private string ConvertTrackingRequestToXml(TrackFieldRequest a_trackingRequest)
{
try
{
var xmlWriterSettings = new XmlWriterSettings
{
Encoding = new UTF8Encoding(false),
Indent = true,
IndentChars = "\t"
};
XmlSerializer xmlSerializer = new XmlSerializer(a_trackingRequest.GetType());
using (StringWriter stringWriter = new StringWriter())
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, xmlWriterSettings))
{
xmlSerializer.Serialize(xmlWriter, a_trackingRequest);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
Logger.LogError("Could not convert tracking request into Xml.", ex);
return null;
}
}
I would prefer not to use the XmlSerializer rather than manually building up the request XML string from a string builder, if possible.
Any ideas?
Thanks in advance for any help you can provide.
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] trackingNumbers = {"5551212699300000962610", "5551212699300000962611", "5551212699300000962612"};
XElement trackFieldRequest = new XElement("TrackFieldRequest", new object[] {
new XAttribute("PASSWORD", "password"),
new XAttribute("USERID", "prodsolclient"),
new XAttribute("APPID", ""),
new XElement("Revision",1),
new XElement("ClientIp", "111.0.0.1")
});
foreach (string trackingNumber in trackingNumbers)
{
trackFieldRequest.Add(new XElement("TrackID", trackingNumber));
}
string xml = trackFieldRequest.ToString();
}
}
}
I made an xml document by using XML Serialization.
It looks like this
<?xml version="1.0" encoding="utf-8"?>
<Course xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<courseName>Comp 1510</courseName>
<backgroundColor>#ffffff</backgroundColor>
<fontColor>#ffffff</fontColor>
<sharingKey>ed35d1f8-6be1-4f87-b77f-c70298e5abbb</sharingKey>
<task type="Assignment">
<taskName>First Task</taskName>
<description>description</description>
<taskDueDate>2010-01-24T12:41:20.0321826-08:00</taskDueDate>
<weight xsi:nil="true" />
<beforeDueDateNotification>30</beforeDueDateNotification>
<outOf>50.4</outOf>
</task>
</Course>
My Code to make this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
[XmlRoot("Course")]
public class MyWrapper
{
public MyWrapper()
{
TaskList = new List<Tasks>();
}
[XmlElement("courseName")]
public string CourseName { get; set; }
[XmlElement("backgroundColor")]
public string BackgroundColor { get; set; }
[XmlElement("fontColor")]
public string FontColor { get; set; }
[XmlElement("sharingKey")]
public Guid SharingKey { get; set; }
[XmlElement("task")]
public List<Tasks> TaskList { get; set; }
}
public class Tasks
{
[XmlAttribute("type")]
public string Type { get; set; }
[XmlElement("taskName")]
public string TaskName { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("taskDueDate")]
public DateTime TaskDueDate { get; set; }
[XmlElement("weight")]
public decimal? Weight { get; set; }
[XmlElement("beforeDueDateNotification")]
public int BeforeDueDateNotification { get; set; }
[XmlElement("outOf")]
public decimal? OutOf { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyWrapper wrap = new MyWrapper();
wrap.CourseName = "Comp 1510";
wrap.FontColor = "#ffffff";
wrap.BackgroundColor = "#ffffff";
wrap.SharingKey = Guid.NewGuid();
Tasks task = new Tasks()
{
TaskName = "First Task",
Type = "Assignment",
TaskDueDate = DateTime.Now,
Description = "description",
BeforeDueDateNotification = 30,
OutOf = 50.4M
};
wrap.TaskList.Add(task);
SerializeToXML(wrap);
var grab = DeserializeFromXML();
foreach (var item in grab)
{
}
}
static public void SerializeToXML(MyWrapper list)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
TextWriter textWriter = new StreamWriter(#"C:\New folder\test.xml");
serializer.Serialize(textWriter, list);
textWriter.Close();
}
static List<MyWrapper> DeserializeFromXML()
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<MyWrapper>));
TextReader textReader = new StreamReader(#"C:\New folder\test.xml");
List<MyWrapper> tasks;
tasks = (List<MyWrapper>)deserializer.Deserialize(textReader);
textReader.Close();
return tasks;
}
}
}
Now when I try to de serialize it I get this error
System.InvalidOperationException was unhandled
Message="There is an error in XML document (2, 2)."
Source="System.Xml"
StackTrace:
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
at ConsoleApplication1.Program.DeserializeFromXML() in C:\Users\chobo2\Desktop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 55
at ConsoleApplication1.Program.Main(String[] args) in C:\Users\chobo2\Desktop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 34
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.InvalidOperationException
Message="<Course xmlns=''> was not expected."
Source="ap72r7cf"
StackTrace:
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderList1.Read5_ArrayOfMyWrapper()
InnerException:
I am not sure why this is happening.
Some Side Questions - Answer please after main problem is answered
I did not want to make a new forum post for these 2 questions unless I have to.
Why is it important to give it an object type? Like why not make all the fields as strings?
[XmlElement("sharingKey")]
public Guid SharingKey { get; set; }
Is it just for when you de serialize that you will get in this case a Guid so that you won't later one have to convert it from a string to a Guid?
If this is correct how about if you get an xml file from someone else and you want to de serialize it how will you know what objects will come out of it? Like for instance how would you know that my "OutOf" is actually a type of nullable decimal? In fact how does C# know that? I don't see anything that would tip it off that this is the type.
When I get it to actually de serialize, I am wondering how do I make my foreach loop. Since I want to go through each of the list of "MyWrapper" objects. But in MyWrapper there is a collection of Task objects. So do I have to make a for loop inside my foreach loop to get at it? Or is there a better way?
Thanks
You're trying to serialize a single instance of MyWrapper, but then deserialize it as a list of them. If you stick to one way or the other (either way) it works fine. For example:
static public void SerializeToXML(MyWrapper wrapper)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<MyWrapper>));
using (TextWriter textWriter = File.CreateText("test.xml"))
{
// Create single-element list
serializer.Serialize(textWriter, new List<MyWrapper>{wrapper});
}
}
static List<MyWrapper> DeserializeFromXML()
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<MyWrapper>));
using (TextReader textReader = File.OpenText("test.xml"))
{
return (List<MyWrapper>)deserializer.Deserialize(textReader);
}
}
or (for a single element):
static public void SerializeToXML(MyWrapper wrapper)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
using (TextWriter textWriter = File.CreateText("test.xml"))
{
serializer.Serialize(textWriter, wrapper);
}
}
static MyWrapper DeserializeFromXML()
{
XmlSerializer deserializer = new XmlSerializer(typeof(MyWrapper));
using (TextReader textReader = File.OpenText("test.xml"))
{
return (MyWrapper)deserializer.Deserialize(textReader);
}
}
You've just got to be consistent, that's all.
Well I guess the problem is: you're serializing a single MyWrapper, but trying to deserialize a List<MyWrapper>
That's not going to work - you serialize a single object into your file, you'll get back a single object MyWrapper when deserializing from that file.
Change your deserialization to:
static MyWrapper DeserializeFromXML()
{
XmlSerializer deserializer = new XmlSerializer(typeof(MyWrapper));
TextReader textReader = new StreamReader(#"C:\New folder\test.xml");
MyWrapper tasks = (MyWrapper)deserializer.Deserialize(textReader);
textReader.Close();
return tasks;
}
and things should work again.