I'm trying to deserialize a reponse from a REST API.
"<FieldListDTO xmlns=\"api.playcento.com/1.0\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
<Allfield>
<FieldDTO>
<Fieldname>Mobile nr</Fieldname>
<Fieldtype>T</Fieldtype>
<Fieldvalue>003241234578</Fieldvalue>
<Fk_id_page>CP584ea74ce5ad4e2d8561d75fc6944f96</Fk_id_page>
<Id_field>FI152dcde5ef9849898b12d6a3f2cdb4ee</Id_field>
<Required>true</Required>
</FieldDTO>
</Allfield>
<Totalcount>1</Totalcount>
</FieldListDTO>"
The Field class:
namespace PlaycentoAPI.Model
{
[XmlRoot("FieldListDTO",Namespace = "api.playcento.com/1.0")]
[XmlType("FieldListDTO")]
public class FieldListDTO
{
public FieldListDTO() { }
[XmlElement("Totalcount")]
public int TotalCount { get; set; }
[XmlArray("Allfield")]
[XmlArrayItem("FieldDTO", typeof(Field))]
public Field[] Field { get; set; }
}
[XmlRoot("FieldDTO", Namespace = "api.paycento.com/1.0")]
[XmlType("FieldDTO")]
public class Field
{
public Field()
{
}
[XmlElement("Id_field")]
public string ID_Field { get; set; }
[XmlElement("Fieldtype")]
public string FieldType { get; set; }
[XmlElement("Fk_id_page")]
public string FK_ID_PAGE { get; set; }
[XmlElement("Required")]
public bool Required { get; set; }
[XmlElement("Fieldname")]
public string FieldName { get; set; }
[XmlElement("Fieldvalue")]
public string FieldValue { get; set; }
}
}
My code which calls the API and deserializes it:
string response = Helper.PerformAndReadHttpRequest(uri, "GET", "");
FieldListDTO myObject;
XmlReaderSettings settings = new XmlReaderSettings();
using (StringReader textReader = new StringReader(response))
{
using (XmlReader xmlReader = XmlReader.Create(textReader, settings))
{
XmlSerializer mySerializer = new XmlSerializer(typeof(FieldListDTO));
myObject = (FieldListDTO)mySerializer.Deserialize(xmlReader);
}
}
return myObject.Field;
In my actual response, I'm getting back 14 FieldDTO's. After deserializing the xml, FieldListDTO myObject contains TotalCount = 14 and Field is an array containing 14 Field's. But all the properties of these fields are NULL (or false).
I'm using the same method for several other API calls. I've compared the classes and the only difference that I see is that the class (Field) has an bool property. So I thought that was the problem. I've changed the bool property to a string but still all the properties were NULL after deserialization.
First thing to catch my eye is that the namespace in your FieldDTO class doesn't match the one in the XML document.
"api.paycento.com/1.0"
"api.playcento.com/1.0"
Related
I'm deserialization the results of a request that has the same tag repeated at multiple levels, I have it working but to do so I'm changing the format of the XML before attempting to deserialize it.
I'm not able to edit the source of the XML to change it to only have Diary at one level.
Is there a way to adjust my XML attributes to handle the deserialization without needing to adjust the response?
XML Response
<?xml version="1.0" ?>
<Root>
<DiaryDetails>
<Diary>
<Diary created_user="value1" created_date="value2" long_text="value3" short_text="value4" entry_type="value5" >Value6</Diary>
</Diary>
<Diary>
<Diary created_user="value7" created_date="value8" long_text="value9" short_text="value10" entry_type="value11" >Value12</Diary>
</Diary>
</DiaryDetails>
</Root>
Class definition
[XmlRoot("DiaryDetails")]
public class Diaries : List<Diary> { }
[XmlRoot("Diary")]
public class Diary
{
[XmlAttribute("created_user")]
public string CreatedBy { get; set; }
[XmlAttribute("created_date")]
public string CreatedDate { get; set; }
[XmlAttribute("long_text")]
public string LongText { get; set; }
[XmlAttribute("short_text")]
public string ShortText { get; set; }
[XmlAttribute("entry_type")]
public string Type { get; set; }
}
Deserialization Method
internal T DeserilaiseObject<T>(string response)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
T DeserilaisedObject;
using (TextReader reader = new StringReader(response))
{
DeserilaisedObject = (T)serializer.Deserialize(reader);
}
return DeserilaisedObject;
}
I'm currently handling this with a string replace:
response = response.Replace("<Diary><Diary", "<Diary").Replace("</Diary></Diary>", "</Diary>");
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication40
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = (Root)serializer.Deserialize(reader);
}
}
[XmlRoot("Root")]
public class Root
{
[XmlArray("DiaryDetails")]
[XmlArrayItem("Diary")]
public List<DiaryMain> diaries { get; set; }
}
public class DiaryMain
{
public Diary Diary { get; set; }
}
[XmlRoot("Diary")]
public class Diary
{
[XmlAttribute("created_user")]
public string CreatedBy { get; set; }
[XmlAttribute("created_date")]
public string CreatedDate { get; set; }
[XmlAttribute("long_text")]
public string LongText { get; set; }
[XmlAttribute("short_text")]
public string ShortText { get; set; }
[XmlAttribute("entry_type")]
public string Type { get; set; }
}
}
Assuming that you are only interested at the attributed Diary elements at the second nesting level you could do this:
// load your xml into a document
var doc = new XmlDocument();
doc.Load("your_xml_file.xml");
// extract the interesting nodes using xpath
var nodes = doc.SelectNodes("//Diary/Diary");
// deserialize the filtered NodeList (yes it's that clunky)
var serializer = new XmlSerializer(typeof(Diary));
var diaries = nodes.Cast<XmlNode>().Select(x => serializer.Deserialize(new XmlNodeReader(x))).Cast<Diary>().ToArray();
I am trying to serialize a C# object into XML so that it could be used as the body of API call. They are very particular about the input they need. I have built the following class to hold the data I need to send over to them. Including attributes and all properties as Elements instead of attributes. They also require that lists include the type="array" I though that creating my own class the implements a List would be the easiest since all lists I give them must have the same attribute. When serialization occurs it serializes the base class of List items but it doesn't include the attribute I want from the derived class.
public class CustomArray<T> : List<T>
{
[XmlAttribute]
public string type { get; set; } = "array";
}
[XmlRoot("message")]
public class MessageBody
{
[XmlArray("Checks"), XmlArrayItem("CheckItem")]
public CustomArray<Check> CheckList { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
[XmlArray("InvoiceList"), XmlArrayItem("Invoice")]
public CustomArray<Invoice> InvoiceList { get; set; }
}
public class Invoice
{
[XmlElement("C_INVOICE_ID")]
public long ID { get; set; }
[XmlElement("C_INVOICE_NUM")]
public string InvoiceNum { get; set; }
}
I then run this code:
// Create a sample object
var message = new MessageBody()
{
CheckList = new CustomArray<Check>
{
new Check
{
CheckNumber = "111",
Amount = 1.00M
},
new Check
{
CheckNumber = "112",
Amount = 2.00M,
InvoiceList = new CustomArray<Invoice>
{
new Invoice
{
ID = 1,
InvoiceNum = "1"
}
}
}
}
};
// Create custom settings
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
};
// Serialize item and print it to console
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw, settings))
{
var serializer = new XmlSerializer(message.GetType());
serializer.Serialize(writer, message, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
Console.WriteLine(sw.ToString());
}
I get this written to the console:
<message>
<Checks>
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList>
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
But I need to get this:
<message>
<Checks type="array">
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList type="array">
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
Thank you for your help!
Here is a dotnetfiddle that I made to show it off. It's not exact but it has the same idea. https://dotnetfiddle.net/ALCX5H
Try following :
[XmlRoot("message")]
public class MessageBody
{
[XmlElement("Checks")]
public Checks Checks { get; set; }
}
public class Checks
{
[XmlAttribute]
public string type { get; set; }
[XmlElement("Checks")]
public List<Check> Checks { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
}
I am having some issue with deserialising some XML to an object list, always getting count 0 and within that "raw data" when inspecting DateAndTimeSlot during debug.
Unfortunately I cannot change the names of these elements.
However when checking the XML I get back, there are DateAndTimeslot objects in the XML.
With other object lists I have all seems fine, without the inclusion of namespaces.
What have I missed?
C# Code:
[XmlRoot("AppointmentAvailabilityStatusResponse")]
public class CheckAppointmentAvailabilityContainer
{
[XmlElement("AppointmentAvailabilityStatusResult")]
public AppointmentAvailabilityStatus appointmentAvailabilityStatus { get; set; }
}
[XmlRoot("AppointmentAvailabilityStatusResult", Namespace = "Appointments")]
public class AppointmentAvailabilityStatus
{
[XmlArray("DateAndTimeSlot", Namespace = "Appointments")]
[XmlArrayItem(typeof(DateAndTimeslot))]
public DateAndTimeSlots DateAndTimeSlot { get; set; }
[XmlElement("RequestedStatus")]
public int RequestedStatus { get; set; }
}
[XmlRoot(ElementName = "DateAndTimeSlot")]
[XmlType("a")]
public class DateAndTimeSlots : List<DateAndTimeslot> { }
[XmlRoot(ElementName = "DateAndTimeslot", Namespace = "Appointments.TO")]
[XmlType("b")] // if included this renames the node to "b" for some reason
public class DateAndTimeslot
{
[XmlElement("Date")]
public string Date { get; set; }
[XmlElement("TimeSlot")]
public string TimeSlot { get; set; }
}
Shortened XML returned that I wish to fully deserialise.
<AppointmentAvailabilityStatusResponse>
<AppointmentAvailabilityStatusResult xmlns:a="Appointments" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:DateAndTimeSlot xmlns:b="Appointments.TO">
<b:DateAndTimeslot>
<b:Date>14/07/2016</b:Date>
<b:TimeSlot>AM</b:TimeSlot>
</b:DateAndTimeslot>
<b:DateAndTimeslot>
<b:Date>14/07/2016</b:Date>
<b:TimeSlot>PM</b:TimeSlot>
</b:DateAndTimeslot>
</a:DateAndTimeSlot>
<a:RequestStatus>0</a:RequestStatus>
</AppointmentAvailabilityStatusResult>
</AppointmentAvailabilityStatusResponse>
XML if I serialise a dummy object - some differences which I'm trying to rectify, not sure if the namespaces are necessary for deserialisation though
<AppointmentAvailabilityStatusResponse>
<AppointmentAvailabilityStatusResult>
<DateAndTimeSlot xmlns=\"Appointments\">
<DateAndTimeslot>
<Date xmlns=\"Appointments.TO\">today</Date>
<TimeSlot xmlns=\"Appointments.TO\">now</TimeSlot>
</DateAndTimeslot>
</DateAndTimeSlot>
<RequestedStatus xmlns=\"Appointments\">0</RequestedStatus>
</AppointmentAvailabilityStatusResult>
</AppointmentAvailabilityStatusResponse>
Deserialiser
public static T DeserializeThis<T>(string cleanXml)
{
//string cleanXml = RemoveBom(dirtyXml);
bool check = cleanXml.TrimStart().StartsWith("<");
if (!string.IsNullOrEmpty(cleanXml) && cleanXml.TrimStart().StartsWith("<"))
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(T));
MatchCollection mc = Regex.Matches(cleanXml, #"</?(\d\w+)");
List<string> elements = new List<string>();
foreach (Match m in mc)
{
string cpval = m.Groups[1].Value;
if (!elements.Contains(cpval)) { elements.Add(cpval); }
}
foreach (string e in elements)
{
cleanXml = cleanXml.Replace(e, "d_" + e);
}
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(cleanXml)))
{
using (StringReader sr = new StringReader(cleanXml))
{
return (T)xs.Deserialize(sr);
}
}
}
catch(XmlException x)
{
var obj = (T)Activator.CreateInstance(typeof(T));
Type type = obj.GetType();
return (T)obj;
}
}
else
{
var obj = (T)Activator.CreateInstance(typeof(T));
Type type = obj.GetType();
// add in the generic derived class property search and assign
return (T)obj;
}
}
Thank you to those who commented above - I finally got it working. Removing the XmlArray and not including Anonymous and IsNullable attributes seemed to be the issue although I am unsure why as it works with all of the other functions I have, serializable possibly doesn't need to be present either.
Working class structure minus the container as that didn't change:
[Serializable()]
[XmlType(AnonymousType = true, Namespace = "")]
public class AppointmentAvailabilityStatusResult : WebserviceMessage
{
[XmlElement("DateAndTimeSlot", Namespace = "Appointments")]
public DateAndTimeSlot DateAndTimeSlot { get; set; }
[XmlElement("RequestedStatus")]
public int RequestedStatus { get; set; }
}
[Serializable()]
[XmlType(AnonymousType = true, Namespace = "Appointments")]
[XmlRoot(ElementName = "DateAndTimeSlot",Namespace = "Appointments", IsNullable = false)]
public class DateAndTimeSlot
{
[XmlElement(ElementName = "DateAndTimeslot", Namespace = "Appointments.TO")]
public List<DateAndTimeslot> DateAndTimeslot { get; set; }
}
[Serializable()]
[XmlType(AnonymousType = true, Namespace = "Appointments.TO")]
[XmlRoot(Namespace = "Appointments.TO", IsNullable = false)]
public class DateAndTimeslot
{
[XmlElement("Date")]
public string Date { get; set; }
[XmlElement("TimeSlot")]
public string TimeSlot { get; set; }
}
I have this class which represent a node TestCase in my XML :
public class TestCase
{
[XmlAttribute("name")]
public string name { get; set; }
public string version { get; set; }
public string verdict { get; set; }
public string objective { get; set; }
public string criteria { get; set; }
public string issue { get; set; }
public string clientcomments { get; set; }
public string authoritycomments { get; set; }
public string sdk { get; set; }
}
I use XmlNode.SelectSingleNode to fetch a specific node in my XML. For info, there are no duplicate nodes (no nodes with the same name attribute) if it matters.
So far, I have this code :
public static TestCase FetchNode(string NodeName, string Path)
{
TestCase testcase = new TestCase();
string[] attr = { "name", "version", "verdict", "objective", "criteria"
, "issue", "clientcomments", "authoritycomments", "sdk" };
string[] attrval = { null, null,null,null,null,null,null,null,null};
XmlDocument doc = new XmlDocument();
doc.Load(Path);
XmlNode node = doc.SelectSingleNode("/TestsList/TestCase[#name='" + NodeName + "']");
for (var i = 0; i == attr.Length - 1;i++)
{
attrval[i] = node[attr[i]].InnerText;
}
testcase.name = attrval[0];
testcase.version = attrval[1];
testcase.verdict = attrval[2];
testcase.objective = attrval[3];
testcase.criteria = attrval[4];
testcase.issue = attrval[5];
testcase.clientcomments = attrval[6];
testcase.authoritycomments = attrval[7];
testcase.sdk = attrval[8];
return testcase;
}
However, this code is not scalable at all, if I change my class structure, I would need to change the function because each element of the class are hardcoded in it.
This is a wide request, but how could I write this function so if I add or remove a string in the class definition of TestCase, I don`t have to change the function FetchNode.
Thank you for your time.
You could use XmlSerializer.Deserialize
Example:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class TestCase
{
[XmlAttribute("name")]
public string name { get; set; }
public string version { get; set; }
public string verdict { get; set; }
public string objective { get; set; }
public string criteria { get; set; }
public string issue { get; set; }
public string clientcomments { get; set; }
public string authoritycomments { get; set; }
public string sdk { get; set; }
}
public class Program
{
public const string XML = #"
<TestCase name='TicketName'>
<name>Jon Nameson</name>
<version>10.1</version>
<verdict>High</verdict>
</TestCase>
";
public static void Main()
{
var doc = new XmlDocument();
doc.LoadXml(XML);
var node = doc.SelectSingleNode("/TestCase");
var serializer = new XmlSerializer(typeof(TestCase));
var testcase = serializer.Deserialize(new StringReader(node.OuterXml)) as TestCase;
Console.WriteLine(testcase.name);
Console.WriteLine(testcase.version);
Console.WriteLine(testcase.verdict);
}
}
DotNetFiddle
You can deserialize directly from your selected XmlNode by combining XmlSerializer with XmlNodeReader using the following extension method:
public static class XmlNodeExtensions
{
public static T Deserialize<T>(this XmlNode element, XmlSerializer serializer = null)
{
using (var reader = new ProperXmlNodeReader(element))
return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
}
class ProperXmlNodeReader : XmlNodeReader
{
// Bug fix from https://stackoverflow.com/questions/30102275/deserialize-object-property-with-stringreader-vs-xmlnodereader
public ProperXmlNodeReader(XmlNode node)
: base(node)
{
}
public override string LookupNamespace(string prefix)
{
return NameTable.Add(base.LookupNamespace(prefix));
}
}
}
This adds an extension method to XmlNode which invokes XmlSerializer to deserialize the selected node to an instance of the generic type T.
Then do:
var testcase = node.Deserialize<TestCase>();
which is identical to:
var testcase = XmlNodeExtensions.Deserialize<TestCase>(node);
In your case the expected root element name of your TestCase class, namely <TestCase>, matches the actual node name. If the node name does not match the expected root element name, you can tell XmlSerializer to expect a different root name by following the instructions in XmlSerializer Performance Issue when Specifying XmlRootAttribute.
I have this xml that return of a web service:
<return>
<LuckNumber>
<Number>00092</Number>
<CodError>00</CodError>
<Serie>019</Serie>
<Number>00093</Number>
<CodError>00</CodError>
<Serie>019</Serie>
<Number>00094</Number>
<CodError>00</CodError>
<Serie>019</Serie>
<Number>00095</Number>
<CodError>00</CodError>
<Serie>019</Serie>
</LuckNumber>
How Can I parse this XML to a typed object using annotations?
I Tried it, but doesn't work:
protected T ProccessResult<T>(string result) {
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringReader reader = new StringReader(result))
{
var resultDeserialize = (T)(serializer.Deserialize(reader));
return resultDeserialize;
}
}
ProccessResult<List<GenerateNumberList>>(STRING_XML_ABOVE)
CLASS TO PARSE:
[XmlRoot("LuckNumber")]
public class GenerateNumberResult
{
[XmlElement("Number")]
public string LuckNumber { get; set; }
[XmlElement("CodError")]
public string CodError{ get; set; }
[XmlElement("Serie")]
public string Serie { get; set; }
}
Can someone help me? Thanks!
The root of your XML is the "return" element. Add a wrapper class that contains your list:
[XmlRoot("return")]
public class ResultWrapper
{
[XmlElement("LuckNumber")]
public List<GenerateNumberResult> numberList;
}
And get the result:
ResultWrapper result = ProccessResult<ResultWrapper>(xml);