I am using C# and have a question in relation to de serializing an XML string.
Here is my code to de serialize:
public object XmlDeserializeFromString(string objectData, Type type)
{
var serializer = new XmlSerializer(type);
object result;
using (TextReader reader = new StringReader(objectData))
{
result = serializer.Deserialize(reader);
}
return result;
}
The following XML works with the above function:
<House>
<address>21 My House</address>
<id>1</id>
<owner>Optimation</owner>
</House>
However, the XML from my Web API application does not:
<House xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MVCwithWebAPIApplication.Models">
<address>21 My House</address>
<id>1</id>
<owner>Optimation</owner>
</House>
How can I get the XmlDeserializeFromString function to work with the XML from my Web API application?
The xml return from web api has a default namespace inside. To deserialize this xml into a memory object (defined by a c# class), XmlSerialize has to know whether the class belongs to that namespace or not. This is specified by the property 'Namespace' in RootAttribute attached to that class. If the namespace in xml matches to the declared namespace in c# class, then the xml is successfully deserialized. Otherwise deserialization fails.
More information about xml namespace please see http://www.w3schools.com/xml/xml_namespaces.asp
below is the demo solution for your reference.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
namespace ConsoleApplication8 {
class Program {
static void Main(string[] args) {
var s1 = "<House><address>21 My House</address><id>1</id><owner>Optimation</owner></House>";
var s2 = "<House xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/MVCwithWebAPIApplication.Models\"><address>21 My House</address><id>1</id><owner>Optimation</owner></House>";
House house = (House)XmlDeserializeFromString(s2, typeof(House));
Console.WriteLine(house.ToString());
Console.Read();
}
public static Object XmlDeserializeFromString(string objectData, Type type) {
var serializer = new XmlSerializer(type);
object result;
using (TextReader reader = new StringReader(objectData)) {
result = serializer.Deserialize(reader);
}
return result;
}
}
//this is the only change
[XmlRoot(Namespace="http://schemas.datacontract.org/2004/07/MVCwithWebAPIApplication.Models")]
public class House {
public String address { get; set; }
public String id { get; set; }
public String owner { get; set; }
public override string ToString() {
return String.Format("address: {0} id: {1} owner: {2}", address, id, owner);
}
}
}
Related
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.
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();
}
}
}
This question already has answers here:
How does one parse XML files? [closed]
(12 answers)
Closed 7 years ago.
I'm trying to read a given file saved in xml, but I'm getting the error "Object reference not set to an instance of an object."
Edit: I cannot use any kind of serialization for this.
Easiest approach you can have for such case is using XmlSerializer. That is not the only approach you can do with .net, as there are XmlReader, XmlTextReader and XDocument to help you with that but XmlSerializer allow you to easily convert data structure to xml and back. Here is an example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace TestXmlSerializer
{
class Program
{
static void Main(string[] args)
{
var g = new Group
{
Name="g2",
Keys = new[] {
new Key { Username="a" },
new Key { Password="b" }
}
};
Group g2;
var xs = new XmlSerializer(typeof(Group));
var s = string.Empty;
using (var tw = new StringWriter()) {
using (var xw = XmlWriter.Create(tw))
xs.Serialize(xw, g);
s = tw.ToString();
}
Console.WriteLine(s);
using (var ms = new StringReader(s))
{
using (var xw = XmlReader.Create(ms))
g2 = xs.Deserialize(xw) as Group;
}
Console.WriteLine(g2.Name);
}
}
[Serializable]
public class Key
{
[XmlAttribute]
public string Title;
[XmlAttribute]
public string Username;
[XmlAttribute]
public string Password;
[XmlAttribute]
public string Url;
[XmlAttribute]
public string Notes;
}
[Serializable]
public class Group
{
[XmlAttribute]
public string Name;
[XmlElement]
public Key[] Keys;
}
}
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.
If I create a class in C#, how can I serialize/deserialize it to a file? Is this somethat that can be done using built in functionality or is it custom code?
XmlSerializer; note that the exact xml names can be controlled through various attributes, but all you really need is:
a public type
with a default constructor
and public read/write members (ideally properties)
Example:
using System;
using System.Xml;
using System.Xml.Serialization;
public class Person {
public string Name { get; set; }
}
static class Program {
static void Main() {
Person person = new Person { Name = "Fred"};
XmlSerializer ser = new XmlSerializer(typeof(Person));
// write
using (XmlWriter xw = XmlWriter.Create("file.xml")) {
ser.Serialize(xw, person);
}
// read
using (XmlReader xr = XmlReader.Create("file.xml")) {
Person clone = (Person) ser.Deserialize(xr);
Console.WriteLine(clone.Name);
}
}
}
You need to use class XmlSerializer. Main methods are Serialize and Deserialize. They accept streams, text readers\writers and other classes.
Code sample:
public class Program
{
public class MyClass
{
public string Name { get; set; }
}
static void Main(string[] args)
{
var myObj = new MyClass { Name = "My name" };
var fileName = "data.xml";
var serializer = new XmlSerializer(typeof(MyClass));
using (var output = new XmlTextWriter(fileName, Encoding.UTF8))
serializer.Serialize(output, myObj);
using (var input = new StreamReader(fileName))
{
var deserialized = (MyClass)serializer.Deserialize(input);
Console.WriteLine(deserialized.Name);
}
Console.WriteLine("Press ENTER to finish");
Console.ReadLine();
}
}