I have the following string to deserialize:
<result>
<error>
<errorcode>0</errorcode>
<errorge>წარმატებით</errorge>
<errorru>Удачно</errorru>
<erroren>successfully</erroren>
<line>89</line>
</error>
<amount>
<gel>1</gel>
</amount>
<user>01001</user>
<service>MyService</service> // here
<data>
<nickname>popcorn2</nickname>
<identification_name>identified</identification_name>
<wallet_code>5554654</wallet_code>
<NationalRate>1</NationalRate>
<RATE>1</RATE>
<GENERATED_AMOUNT>1</GENERATED_AMOUNT>
<CURRENCY>GEL</CURRENCY>
</data>
<accoutant>
<agentBenefit>0</agentBenefit>
<agentCommission>0.44</agentCommission>
<clientCommission>0</clientCommission>
</accoutant>
<service>
<min_amount>0.49</min_amount>
<max_amount>1500.00</max_amount>
<currency>GEL</currency>
</service>
<avance>-134206.1500</avance>
<operation_status>0</operation_status>
</result>
as you noticed it has two tags named "service" and they have different contents
here is the class that I've been using
[XmlRoot(ElementName = "error")]
public class ErrorInfo
{
[XmlElement(ElementName = "errorcode")]
public string Errorcode { get; set; }
[XmlElement(ElementName = "errorge")]
public string Errorge { get; set; }
[XmlElement(ElementName = "errorru")]
public string Errorru { get; set; }
[XmlElement(ElementName = "erroren")]
public string Erroren { get; set; }
[XmlElement(ElementName = "line")]
public string Line { get; set; }
}
[XmlRoot(ElementName = "amount")]
public class Amount
{
[XmlElement(ElementName = "gel")]
public string Gel { get; set; }
}
[XmlRoot(ElementName = "data")]
public class Data
{
[XmlElement(ElementName = "nickname")]
public string Nickname { get; set; }
[XmlElement(ElementName = "identification_name")]
public string Identification_name { get; set; }
[XmlElement(ElementName = "wallet_code")]
public string Wallet_code { get; set; }
[XmlElement(ElementName = "NationalRate")]
public string NationalRate { get; set; }
[XmlElement(ElementName = "RATE")]
public string RATE { get; set; }
[XmlElement(ElementName = "GENERATED_AMOUNT")]
public string GENERATED_AMOUNT { get; set; }
[XmlElement(ElementName = "CURRENCY")]
public string CURRENCY { get; set; }
}
[XmlRoot(ElementName = "accoutant")]
public class Accoutant
{
[XmlElement(ElementName = "agentBenefit")]
public string AgentBenefit { get; set; }
[XmlElement(ElementName = "agentCommission")]
public string AgentCommission { get; set; }
[XmlElement(ElementName = "clientCommission")]
public string ClientCommission { get; set; }
}
[XmlRoot(ElementName = "service")]
public class Service
{
[XmlElement(ElementName = "min_amount")]
public string Min_amount { get; set; }
[XmlElement(ElementName = "max_amount")]
public string Max_amount { get; set; }
[XmlElement(ElementName = "currency")]
public string Currency { get; set; }
}
[XmlRoot(ElementName = "result")]
public class Result
{
[XmlElement(ElementName = "error")]
public ErrorInfo Error { get; set; }
[XmlElement(ElementName = "amount")]
public Amount Amount { get; set; }
[XmlElement(ElementName = "user")]
public string User { get; set; }
[XmlElement(ElementName = "data")]
public Data Data { get; set; }
[XmlElement(ElementName = "accoutant")]
public Accoutant Accoutant { get; set; }
[XmlElement(ElementName = "service")]
public Service[] Service { get; set; }
[XmlElement(ElementName = "avance")]
public string Avance { get; set; }
[XmlElement(ElementName = "operation_status")]
public string Operation_status { get; set; }
}
I tried to handle service tags as array but it could not work. I was able to get values from the last tag , but not from the first one. both tags are neccessary so i am wondering if there is a way to get values from both tags ?
Visual Studio has this really great feature where you can put your XML in the clipboard and then do Edit -> Paste special -> Paste XML as classes.
If you do that with your XML, it generates the following class for the service tag:
/// <remarks/>
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class resultService
{
private decimal min_amountField;
private decimal max_amountField;
private string currencyField;
private string[] textField;
/// <remarks/>
public decimal min_amount
{
get
{
return this.min_amountField;
}
set
{
this.min_amountField = value;
}
}
/// <remarks/>
public decimal max_amount
{
get
{
return this.max_amountField;
}
set
{
this.max_amountField = value;
}
}
/// <remarks/>
public string currency
{
get
{
return this.currencyField;
}
set
{
this.currencyField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Text
{
get
{
return this.textField;
}
set
{
this.textField = value;
}
}
}
If you want to do it manually, the secret seems to be to add a field with the XmlTextAttribute attribute.
You can not have two different elements with the same name. It won't know which tag belongs where, you have to alter the name to "MyServiceName" or something similar.
Change your classes as follows:
[XmlRoot(ElementName = "result")]
public class Result
{
// no need XmlElement attribute
public ServiceComplex ServiceComplex { get; set; }
// no need XmlElement attribute
public string ServiceSimple { get; set; }
// other properties
}
// no need XmlRoot attribute
public class ServiceComplex
{
[XmlElement(ElementName = "min_amount")]
public string Min_amount { get; set; }
[XmlElement(ElementName = "max_amount")]
public string Max_amount { get; set; }
[XmlElement(ElementName = "currency")]
public string Currency { get; set; }
}
Instead of ServiceComplex and ServiceSimple, choose the names that suit you.
Subscribe XmlSerializer to UnknownElement event:
var xs = new XmlSerializer(typeof(Result));
xs.UnknownElement += Xs_UnknownElement;
private static void Xs_UnknownElement(object sender, XmlElementEventArgs e)
{
if (e.Element.Name == "service")
{
var result = (Result)e.ObjectBeingDeserialized;
if (e.Element.ChildNodes.Count == 1)
{
result.ServiceSimple = e.Element.InnerText;
}
else
{
result.ServiceComplex = new ServiceComplex
{
Min_amount = e.Element.SelectSingleNode("min_amount").InnerText,
Max_amount = e.Element.SelectSingleNode("max_amount").InnerText,
Currency = e.Element.SelectSingleNode("currency").InnerText
};
}
}
}
In the event handler, we manually populate the properties of our class from xml.
Related
Trying to Deserialise XML response to C#. but getting error as specified below.
My code:
IRestResponse result = Client.Execute<StudentPersonal>(new RestRequest(Method.POST)
.AddHeader("Accept", " application/xml")
.AddHeader("Authorization", "Basic Y2xaTWNqZFFlbFF4YVU1TlFtUnZOV3hXTkhadmNHaFhPSGxTT0Rka1VIcFhhRTl5TWtWQ05rRjNUMlp5VlUxWlNYa3hjbEU1V1RGUmMxTkNOMlZWVlRvNk9nPT06eUkxdERIanNVcGxLUUpzUlNXNTVacXpRVUFGQ3JrZzFwUGxUbDhUTmRQaFU0Z0xsTFJlQkVxTmhzZml3TnpaVA==")
.AddHeader("Content-Type", "application/xml")
.AddParameter("application/xml", body, ParameterType.RequestBody));
StudentPersonal firstResponse = new StudentPersonal();
StringReader srt = new StringReader(result.Content);
XmlSerializer serializer = new XmlSerializer(typeof(StudentPersonal), new XmlRootAttribute("StudentPersonal"));
firstResponse = (StudentPersonal)serializer.Deserialize(srt);
Console.WriteLine(firstResponse);
Assert.AreEqual("Created", result.StatusCode.ToString());
My classes:
[XmlRoot(ElementName = "StudentPersonal")]
public class StudentPersonal
{
//// XmlSerializer serializer = new XmlSerializer(typeof(StudentPersonal));
//// using (StringReader reader = new StringReader(xml))
//// {
//// var test = (StudentPersonal)serializer.Deserialize(reader);
//// }
[XmlElement(ElementName = "LocalId")]
public double LocalId { get; set; }
[XmlElement(ElementName = "StateProvinceId")]
public int StateProvinceId { get; set; }
[XmlElement(ElementName = "ElectronicIdList")]
public object ElectronicIdList { get; set; }
[XmlElement(ElementName = "OtherIdList")]
public OtherIdList OtherIdList { get; set; }
[XmlElement(ElementName = "PersonInfo")]
public PersonInfo PersonInfo { get; set; }
[XmlElement(ElementName = "GiftedTalented")]
public string GiftedTalented { get; set; }
[XmlElement(ElementName = "EconomicDisadvantage")]
public string EconomicDisadvantage { get; set; }
[XmlElement(ElementName = "ESL")]
public string ESL { get; set; }
[XmlElement(ElementName = "YoungCarersRole")]
public string YoungCarersRole { get; set; }
[XmlElement(ElementName = "Disability")]
public string Disability { get; set; }
[XmlElement(ElementName = "IntegrationAide")]
public string IntegrationAide { get; set; }
[XmlElement(ElementName = "EducationSupport")]
public string EducationSupport { get; set; }
[XmlElement(ElementName = "Sensitive")]
public string Sensitive { get; set; }
[XmlAttribute(AttributeName = "xsd")]
public string Xsd { get; set; }
[XmlAttribute(AttributeName = "xsi")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "RefId")]
public string RefId { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "OtherId")]
public class OtherId
{
[XmlAttribute(AttributeName = "Type")]
public string Type { get; set; }
[XmlText]
public int Text { get; set; }
}
[XmlRoot(ElementName = "OtherIdList")]
public class OtherIdList
{
[XmlElement(ElementName = "OtherId")]
public List<OtherId> OtherId { get; set; }
}
[XmlRoot(ElementName = "Name")]
public class Name
{
[XmlElement(ElementName = "Title")]
public string Title { get; set; }
[XmlElement(ElementName = "FamilyName")]
public string FamilyName { get; set; }
[XmlElement(ElementName = "GivenName")]
public string GivenName { get; set; }
[XmlElement(ElementName = "MiddleName")]
public string MiddleName { get; set; }
[XmlElement(ElementName = "FamilyNameFirst")]
public string FamilyNameFirst { get; set; }
[XmlElement(ElementName = "PreferredFamilyName")]
public string PreferredFamilyName { get; set; }
[XmlElement(ElementName = "PreferredFamilyNameFirst")]
public string PreferredFamilyNameFirst { get; set; }
[XmlElement(ElementName = "PreferredGivenName")]
public string PreferredGivenName { get; set; }
[XmlElement(ElementName = "FullName")]
public string FullName { get; set; }
[XmlAttribute(AttributeName = "Type")]
public string Type { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "EnglishProficiency")]
public class EnglishProficiency
{
[XmlElement(ElementName = "Code")]
public int Code { get; set; }
}
[XmlRoot(ElementName = "Language")]
public class Language
{
[XmlElement(ElementName = "Code")]
public int Code { get; set; }
[XmlElement(ElementName = "LanguageType")]
public int LanguageType { get; set; }
}
[XmlRoot(ElementName = "LanguageList")]
public class LanguageList
{
[XmlElement(ElementName = "Language")]
public List<Language> Language { get; set; }
}
[XmlRoot(ElementName = "DwellingArrangement")]
public class DwellingArrangement
{
[XmlElement(ElementName = "Code")]
public int Code { get; set; }
}
[XmlRoot(ElementName = "Religion")]
public class Religion
{
[XmlElement(ElementName = "Code")]
public int Code { get; set; }
}
[XmlRoot(ElementName = "ReligiousEvent")]
public class ReligiousEvent
{
[XmlElement(ElementName = "Type")]
public string Type { get; set; }
[XmlElement(ElementName = "Date")]
public DateTime Date { get; set; }
}
[XmlRoot(ElementName = "ReligiousEventList")]
public class ReligiousEventList
{
[XmlElement(ElementName = "ReligiousEvent")]
public ReligiousEvent ReligiousEvent { get; set; }
}
[XmlRoot(ElementName = "Demographics")]
public class Demographics
{
[XmlElement(ElementName = "IndigenousStatus")]
public int IndigenousStatus { get; set; }
[XmlElement(ElementName = "Sex")]
public int Sex { get; set; }
[XmlElement(ElementName = "BirthDate")]
public DateTime BirthDate { get; set; }
[XmlElement(ElementName = "BirthDateVerification")]
public int BirthDateVerification { get; set; }
[XmlElement(ElementName = "CountryArrivalDate")]
public DateTime CountryArrivalDate { get; set; }
[XmlElement(ElementName = "EnglishProficiency")]
public EnglishProficiency EnglishProficiency { get; set; }
[XmlElement(ElementName = "LanguageList")]
public LanguageList LanguageList { get; set; }
[XmlElement(ElementName = "DwellingArrangement")]
public DwellingArrangement DwellingArrangement { get; set; }
[XmlElement(ElementName = "Religion")]
public Religion Religion { get; set; }
[XmlElement(ElementName = "ReligiousEventList")]
public ReligiousEventList ReligiousEventList { get; set; }
[XmlElement(ElementName = "PermanentResident")]
public string PermanentResident { get; set; }
[XmlElement(ElementName = "VisaSubClass")]
public int VisaSubClass { get; set; }
[XmlElement(ElementName = "VisaStatisticalCode")]
public string VisaStatisticalCode { get; set; }
[XmlElement(ElementName = "VisaExpiryDate")]
public DateTime VisaExpiryDate { get; set; }
[XmlElement(ElementName = "LBOTE")]
public string LBOTE { get; set; }
[XmlElement(ElementName = "ImmunisationCertificateStatus")]
public string ImmunisationCertificateStatus { get; set; }
[XmlElement(ElementName = "CulturalBackground")]
public int CulturalBackground { get; set; }
[XmlElement(ElementName = "MaritalStatus")]
public int MaritalStatus { get; set; }
[XmlElement(ElementName = "MedicareNumber")]
public int MedicareNumber { get; set; }
}
[XmlRoot(ElementName = "PhoneNumber")]
public class PhoneNumber
{
[XmlElement(ElementName = "Number")]
public int Number { get; set; }
[XmlElement(ElementName = "ListedStatus")]
public string ListedStatus { get; set; }
[XmlAttribute(AttributeName = "Type")]
public int Type { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "PhoneNumberList")]
public class PhoneNumberList
{
[XmlElement(ElementName = "PhoneNumber")]
public PhoneNumber PhoneNumber { get; set; }
}
[XmlRoot(ElementName = "Email")]
public class Email
{
[XmlAttribute(AttributeName = "Type")]
public int Type { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "EmailList")]
public class EmailList
{
[XmlElement(ElementName = "Email")]
public List<Email> Email { get; set; }
}
[XmlRoot(ElementName = "PersonInfo")]
public class PersonInfo
{
[XmlElement(ElementName = "Name")]
public Name Name { get; set; }
[XmlElement(ElementName = "Demographics")]
public Demographics Demographics { get; set; }
[XmlElement(ElementName = "AddressList")]
public object AddressList { get; set; }
[XmlElement(ElementName = "PhoneNumberList")]
public PhoneNumberList PhoneNumberList { get; set; }
[XmlElement(ElementName = "EmailList")]
public EmailList EmailList { get; set; }
}
Below is the response XML details from Result.content:
<StudentPersonal xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" RefId=\"9ec476fd-09b5-4b2b-967a-fb80b207526e\" xmlns=\"http://www.sifassociation.org/datamodel/au/3.4\">
<LocalId>2432717276450097998</LocalId>
<StateProvinceId>674361</StateProvinceId>
<ElectronicIdList />
<OtherIdList>
<OtherId Type=\"MazeKey\">674363</OtherId>
<OtherId Type=\"VETUSI\">674362</OtherId>
<OtherId Type=\"UPN\">674363</OtherId>
</OtherIdList>
<PersonInfo>
<Name Type=\"LGL\">
<Title>Ms</Title>
<FamilyName>Gutteridge2</FamilyName>
<GivenName>Ferdinanda2</GivenName>
<MiddleName>Creamer1</MiddleName>
<FamilyNameFirst>N</FamilyNameFirst>
<PreferredFamilyName>Gutteridge</PreferredFamilyName>
<PreferredFamilyNameFirst>N</PreferredFamilyNameFirst>
<PreferredGivenName>Ferdinanda</PreferredGivenName>
<FullName> Ferdinanda Gutteridge</FullName>
</Name>
<Demographics>
<IndigenousStatus>1</IndigenousStatus>
<Sex>2</Sex>
<BirthDate>2006-03-21</BirthDate>
<BirthDateVerification>1004</BirthDateVerification>
<CountryArrivalDate>2008-09-17</CountryArrivalDate>
<EnglishProficiency>
<Code>1</Code>
</EnglishProficiency>
<LanguageList>
<Language>
<Code>1201</Code>
<LanguageType>3</LanguageType>
</Language>
<Language>
<Code>4303</Code>
<LanguageType>1</LanguageType>
</Language>
<Language>
<Code>4303</Code>
<LanguageType>1</LanguageType>
</Language>
</LanguageList>
<DwellingArrangement>
<Code>1671</Code>
</DwellingArrangement>
<Religion>
<Code>0002</Code>
</Religion>
<ReligiousEventList>
<ReligiousEvent>
<Type>Confirmation</Type>
<Date>2018-07-17</Date>
</ReligiousEvent>
</ReligiousEventList>
<PermanentResident>P</PermanentResident>
<VisaSubClass>124</VisaSubClass>
<VisaStatisticalCode>VisaStatisticalCode</VisaStatisticalCode>
<VisaExpiryDate>2019-01-17</VisaExpiryDate>
<LBOTE>N</LBOTE>
<ImmunisationCertificateStatus>C</ImmunisationCertificateStatus>
<CulturalBackground>0901</CulturalBackground>
<MaritalStatus>3</MaritalStatus>
<MedicareNumber>67436</MedicareNumber>
</Demographics>
<AddressList />
<PhoneNumberList>
<PhoneNumber Type=\"0350\">
<Number>12367436</Number>
<ListedStatus>U</ListedStatus>
</PhoneNumber>
</PhoneNumberList>
<EmailList>
<Email Type=\"01\">person67436#email1.com.au</Email>
<Email Type=\"02\">person67436#email2.com.au</Email>
<Email Type=\"03\">person67436#email3.com.au</Email>
</EmailList>
</PersonInfo>
<GiftedTalented>N</GiftedTalented>
<EconomicDisadvantage>N</EconomicDisadvantage>
<ESL>N</ESL>
<YoungCarersRole>N</YoungCarersRole>
<Disability>N</Disability>
<IntegrationAide>N</IntegrationAide>
<EducationSupport>U</EducationSupport>
<Sensitive>N</Sensitive>
</StudentPersonal>
Error is from the above XML response. Getting Error
There is an error in XML document (1, 2). InvalidOperationException: <StudentPersonal xmlns='http://www.sifassociation.org/datamodel/au/3.4'> was not expected.
There seems to be two issues.
Add the namespace to your class:
[XmlRoot(ElementName = "StudentPersonal", Namespace = "http://www.sifassociation.org/datamodel/au/3.4")]
public class StudentPersonal
...
In the XML, all " are escaped with \.
<StudentPersonal xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" RefId=\"9ec476fd-09b5-4b2b-967a-fb80b207526e\" xmlns=\"http://www.sifassociation.org/datamodel/au/3.4\">
...
</StudentPersonal>
This also occurs in other places throughout the XML. Such as:
<PhoneNumber Type=\"0350\">
To resolve the issue, replace all occurrences of \" with ".
<StudentPersonal xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" RefId="9ec476fd-09b5-4b2b-967a-fb80b207526e" xmlns="http://www.sifassociation.org/datamodel/au/3.4">
...
</StudentPersonal>
and
<PhoneNumber Type="0350">
Here's a method that can be used to deserialize the XML:
DeserializeXMLStringToObject:
public static T DeserializeXMLStringToObject<T>(string xmlData)
{
T rObject = default(T);
try
{
if (string.IsNullOrEmpty(xmlData))
{
return default(T);
}
//replace \" with "
string xmlDataSanitized = xmlData.Replace("\\\"", "\"");
using (System.IO.StringReader reader = new System.IO.StringReader(xmlDataSanitized))
{
//add the namespace to the class instead of adding it here
//System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T), "http://www.sifassociation.org/datamodel/au/3.4");
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
rObject = (T)serializer.Deserialize(reader);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
throw ex;
}
return rObject;
}
Usage:
private StudentPersonal _studentPersonal = null;
...
_studentPersonal = DeserializeXMLStringToObject<StudentPersonal>(result.Content);
//for testing, display some of the data
//System.Diagnostics.Debug.WriteLine("LocalId: " + _studentPersonal.LocalId);
//System.Diagnostics.Debug.WriteLine("GivenName: " + _studentPersonal.PersonInfo.Name.GivenName);
I have an XML document that starts with the following root:
<?xml version="1.0" encoding="UTF-8"?>
<JPK
xmlns="http://jpk.mf.gov.pl/wzor/2019/09/27/09271/"
xmlns:etd="http://crd.gov.pl/xml/schematy/dziedzinowe/mf/2018/08/24/eD/DefinicjeTypy/">
<Naglowek>
<KodFormularza kodSystemowy="SomeCode" wersjaSchemy="1-0">SomeCode</KodFormularza>
<WariantFormularza>3</WariantFormularza>
<CelZlozenia>1</CelZlozenia>
<DataWytworzeniaJPK>2021-06-30T15:57:53</DataWytworzeniaJPK>
<DataOd>2021-05-01</DataOd>
<DataDo>2021-05-31</DataDo>
<KodUrzedu>0000</KodUrzedu>
</Naglowek>
<Podmiot1>
<IdentyfikatorPodmiotu>
<etd:NIP>111111</etd:NIP>
<etd:PelnaNazwa>SomeName</etd:PelnaNazwa>
</IdentyfikatorPodmiotu>
<AdresPodmiotu>
<etd:Wojewodztwo>voivodeship</etd:Wojewodztwo>
<etd:KodKraju>PL</etd:KodKraju>
<etd:Powiat>Danzig</etd:Powiat>
<etd:Gmina>Danzig</etd:Gmina>
<etd:Ulica>SomeStreet</etd:Ulica>
<etd:NrDomu>81</etd:NrDomu>
<etd:NrLokalu>1</etd:NrLokalu>
<etd:Miejscowosc>Danzig</etd:Miejscowosc>
<etd:KodPocztowy>10-101</etd:KodPocztowy>
</AdresPodmiotu>
</Podmiot1>
<!-- These can be many in the same element, there's no root for this list -->
<Faktura>
<KodWaluty>PLN</KodWaluty>
<P_1>2021-05-04</P_1>
<P_2A>11 / 1111</P_2A>
<P_3A>Some Company</P_3A>
<P_3B>Some Address</P_3B>
<P_3C>Some Name</P_3C>
<P_3D>Some Other Address</P_3D>
<P_4B>Phone1</P_4B>
<P_5B>Phone2</P_5B>
<P_13_1>1.00</P_13_1>
<P_14_1>1.25</P_14_1>
<P_15>SomeDecimalNumber</P_15>
<P_16>false</P_16>
<P_17>false</P_17>
<P_18>false</P_18>
<P_18A>false</P_18A>
<P_19>false</P_19>
<P_20>false</P_20>
<P_21>false</P_21>
<P_22>false</P_22>
<P_23>false</P_23>
<P_106E_2>false</P_106E_2>
<P_106E_3>false</P_106E_3>
<RodzajFaktury>InvoiceType</RodzajFaktury>
</Faktura>
<!-- These can be many in the same element, there's no root for this list -->
<FakturaCtrl>
<LiczbaFaktur>1</LiczbaFaktur>
<WartoscFaktur>2.00</WartoscFaktur>
</FakturaCtrl>
<!-- These can be many in the same element, there's no root for this list -->
<FakturaWiersz>
<P_2B>04/123</P_2B>
<P_7>Text</P_7>
<P_8B>1.000000</P_8B>
<P_9A>7.00</P_9A>
<P_11>7.00</P_11>
<P_12>11</P_12>
</FakturaWiersz>
<FakturaWierszCtrl>
<LiczbaWierszyFaktur>11</LiczbaWierszyFaktur>
<WartoscWierszyFaktur>11.2</WartoscWierszyFaktur>
</FakturaWierszCtrl>
</JPK>
It has capital letters. I have no influence on its definition, I need to adjust myself.
I've written a class for it:
[XmlRoot(ElementName = "JPK", Namespace = "http://jpk.mf.gov.pl/wzor/2019/09/27/09271/", IsNullable = false)]
public class Jpk
{
public Jpk() { }
[XmlElement(ElementName = "Naglowek")]
public JpkHeader Header { get; set; }
[XmlElement(ElementName = "Podmiot1")]
public JpkSubject Subject { get; set; }
[XmlElement(ElementName = "Faktura")]
public JpkInvoice[] Invoices { get; set; }
[XmlElement(ElementName = "FakturaCtrl")]
public JpkInvoiceControl[] InvoiceControls { get; set; }
[XmlElement(ElementName = "FakturaWiersz")]
public JpkInvoiceRow[] InvoiceRows { get; set; }
[XmlElement(ElementName = "FakturaWierszCtrl")]
public JpkInvoiceRowControl InvoiceRowControl { get; set; }
}
public class JpkHeader
{
[XmlElement(ElementName = "KodFormularza")]
public string FormCode { get; set; }
[XmlElement(ElementName = "WariantFormularza")]
public string Variant { get; set; }
[XmlElement(ElementName = "CelZlozenia")]
public int Purpose { get; set; }
[XmlElement(ElementName = "DataWytworzeniaJPK")]
public DateTime CreationDate { get; set; }
[XmlElement(ElementName = "DataOd")]
public DateTime DateFrom { get; set; }
[XmlElement(ElementName = "DataDo")]
public DateTime DateTo { get; set; }
[XmlElement(ElementName = "KodUrzedu")]
public string OfficeCode { get; set; }
}
public class JpkInvoice
{
[XmlElement(ElementName = "KodWaluty")]
public string CurrencyCode { get; set; }
public DateTime P_1 { get; set; }
public string P_2A { get; set; }
public string P_3A { get; set; }
public string P_3B { get; set; }
public string P_3C { get; set; }
public string P_3D { get; set; }
public string P_4B { get; set; }
public string P_5B { get; set; }
public decimal P_13_1 { get; set; }
public decimal P_14_1 { get; set; }
public decimal P_15 { get; set; }
public bool P_16 { get; set; }
public bool P_17 { get; set; }
public bool P_18 { get; set; }
public bool P_18A { get; set; }
public bool P_19 { get; set; }
public bool P_20 { get; set; }
public bool P_21 { get; set; }
public bool P_22 { get; set; }
public bool P_23 { get; set; }
public bool P_106E_2 { get; set; }
public bool P_106E_3 { get; set; }
[XmlElement(ElementName = "RodzajFaktury")]
public string InvoiceType { get; set; }
}
public class JpkInvoiceControl
{
[XmlElement(ElementName = "LiczbaFaktur")]
public int InvoiceAmount { get; set; }
[XmlElement(ElementName = "WartoscFaktur")]
public decimal InvoiceValue { get; set; }
}
public class JpkInvoiceRow
{
public string P_2B { get; set; }
public string P_7 { get; set; }
public double P_8B { get; set; }
public decimal P_9A { get; set; }
public decimal P_11 { get; set; }
public int P_12 { get; set; }
}
public class JpkInvoiceRowControl
{
[XmlElement(ElementName = "LiczbaWierszyFaktur")]
public int InvoiceRowAmount { get; set; }
[XmlElement(ElementName = "WartoscWierszyFaktur")]
public decimal InvoiceRowSum { get; set; }
}
public class JpkSubject
{
[XmlElement(ElementName = "IdentyfikatorPodmiotu")]
public SubjectID SubjectId { get; set; }
[XmlElement(ElementName = "AdresPodmiotu")]
public SubjectAddress Address { get; set; }
}
public class SubjectID
{
[XmlElement(ElementName = "etd:NIP")]
public string NIP { get; set; }
[XmlElement(ElementName = "etd:PelnaNazwa")]
public string FullName { get; set; }
}
public class SubjectAddress
{
[XmlElement(ElementName = "etd:KodKraju")]
public string CountryCode { get; set; }
[XmlElement(ElementName = "etd:Wojewodztwo")]
public string Province { get; set; }
[XmlElement(ElementName = "etd:Powiat")]
public string District { get; set; }
[XmlElement(ElementName = "etd:Gmina")]
public string Community { get; set; }
[XmlElement(ElementName = "etd:Ulica")]
public string StreetName { get; set; }
[XmlElement(ElementName = "etd:NrDomu")]
public int HouseNumber { get; set; }
[XmlElement(ElementName = "etd:NrLokalu")]
public int FlatNumber { get; set; }
[XmlElement(ElementName = "etd:Miejscowosc")]
public string City { get; set; }
[XmlElement(ElementName = "etd:KodPocztowy")]
public string PostalCode { get; set; }
}
And I have my code that deserializes it:
var serializer = new XmlSerializer(typeof(Etc), new XmlRootAttribute("JPK"));
var streamReader = new StreamReader(#"C:\_NotInGit\sample.xml");
var smth = (Jpk)serializer.Deserialize(streamReader);
When I run this code I get
System.InvalidOperationException: 'There is an error in XML document (2, 2).'
InvalidOperationException: was not expected.
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
at MyProject.Program.Main(String[] args) in C:\MyProject\Program.cs:line 15
I run my queries through the Internet:
I ensured that the XmlRoot element has the exact capitalized version I encounter in the file
I put the namespace in
I tried adding Serializable, no changes
I ensured I pass the XmlRootAttribute to the serializer constructor
My spider-sense tells me it might have to do with the second definition, but I'm not sure how to approach it. I tried to remove it (for science purposes), but it did not change the response.
Is there anything else I am missing?
Spoiler alert: It was (also) about the second namespace definition. Thankfully the accepted answer showed me how to declare it, and what to do with the etd part. That, and the removal of new XmlRootAttribute("JPK") from code.
When attempting to debug a XML deserialization issue, I find it helpful to serialize the class that I've created using some sample data. Then, I check if the data that I serialized (saved to a XML file) is the same as the XML file that I'm trying to deserialize.
In the XML data you posted, within Faktura, is the following:
<P_15>SomeDecimalNumber</P_15>
Then in class JpkInvoice is the following:
public decimal P_15 { get; set; }
This is an issue because the value in the XML file is a string, whereas the property is declared as decimal. Either the property data type needs to be string or the value in the XML file needs to be changed to a valid decimal value. For testing, in the XML, I replaced <P_15>SomeDecimalNumber</P_15> with <P_15>0</P_15>.
I've tested the following code with the XML that you provided - with the XML data modification described above. I changed the property names to match what's in the XML file as I find it easier that way. You can rename them if you like. Also, I prefer to use List instead of an array, so you'll notice that in the code as well.
In the code below, the following using statements are necessary:
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
Create a class (name: Jpk)
Class: Jpk
[XmlRoot(ElementName = "JPK", Namespace = "http://jpk.mf.gov.pl/wzor/2019/09/27/09271/", IsNullable = false)]
public class Jpk
{
[XmlElement(ElementName = "Naglowek")]
public JpkNaglowek Naglowek { get; set; } = new JpkNaglowek(); //Header
[XmlElement(ElementName = "Podmiot1")]
public JpkPodmiot1 Podmiot1 { get; set; } = new JpkPodmiot1(); //Subject
[XmlElement(ElementName = "Faktura")]
public List<JpkFaktura> Faktura { get; set; } = new List<JpkFaktura>(); //Invoices
[XmlElement(ElementName = "FakturaCtrl")]
public List<JpkFakturaCtrl> FakturaCtrl { get; set; } = new List<JpkFakturaCtrl>(); //InvoiceControls
[XmlElement(ElementName = "FakturaWiersz")]
public List<JpkFakturaWiersz> FakturaWiersz { get; set; } = new List<JpkFakturaWiersz>(); //InvoiceRows
[XmlElement(ElementName = "FakturaWierszCtrl")]
public List<JpkFakturaWierszCtrl> FakturaWierszCtrl { get; set; } = new List<JpkFakturaWierszCtrl>(); //InvoiceRowControl
}
Class: JpkFaktura
public class JpkFaktura
{
private string p_1 = string.Empty;
[XmlIgnore]
public DateTime P_1Dt { get; private set; } = DateTime.MinValue; //value of P_1 as DateTime
[XmlElement]
public string KodWaluty { get; set; }
[XmlElement]
public string P_1
{
get
{
return this.p_1;
}
set
{
this.p_1 = value;
//try to convert to DateTime
DateTime dt = DateTime.MinValue;
DateTime.TryParseExact(value, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dt);
//set value
this.P_1Dt = dt;
}
}
[XmlElement]
public string P_2A { get; set; }
[XmlElement]
public string P_3A { get; set; }
[XmlElement]
public string P_3B { get; set; }
[XmlElement]
public string P_3C { get; set; }
[XmlElement]
public string P_3D { get; set; }
[XmlElement]
public string P_4B { get; set; }
[XmlElement]
public string P_5B { get; set; }
[XmlElement]
public decimal P_13_1 { get; set; }
[XmlElement]
public decimal P_14_1 { get; set; }
[XmlElement]
public decimal P_15 { get; set; }
[XmlElement]
public bool P_16 { get; set; } = false;
[XmlElement]
public bool P_17 { get; set; } = false;
[XmlElement]
public bool P_18 { get; set; } = false;
[XmlElement]
public bool P_18A { get; set; } = false;
[XmlElement]
public bool P_19 { get; set; } = false;
[XmlElement]
public bool P_20 { get; set; } = false;
[XmlElement]
public bool P_21 { get; set; } = false;
[XmlElement]
public bool P_22 { get; set; } = false;
[XmlElement]
public bool P_23 { get; set; } = false;
[XmlElement]
public bool P_106E_2 { get; set; } = false;
[XmlElement]
public bool P_106E_3 { get; set; } = false;
[XmlElement]
public string RodzajFaktury { get; set; }
}
Note: In the code above you'll notice a property named P_1Dt. It's a public property that holds the value of P_1 as DateTime. By specifying [XmlIgnore] this property will be ignored during deserialization/serialization. In the XML, the format of the data is yyyy-MM-dd (ex: 2021-05-01), which is why it's necessary to specify the data type as a string. If the data type is specified as DateTime, when serialized the data in the XML file would look like yyyy-MM-ddTHH:mm:ss (ex: 2021-05-01T00:00:00)
Class: JpkFakturaCtrl
public class JpkFakturaCtrl
{
[XmlElement]
public decimal LiczbaFaktur { get; set; }
[XmlElement]
public decimal WartoscFaktur { get; set; }
}
Class: JpkFakturaWiersz
public class JpkFakturaWiersz
{
[XmlElement]
public string P_2B { get; set; }
[XmlElement]
public string P_7 { get; set; }
[XmlElement]
public decimal P_8B { get; set; }
[XmlElement]
public decimal P_9A { get; set; }
[XmlElement]
public decimal P_11 { get; set; }
[XmlElement]
public int P_12 { get; set; }
}
Class: JpkFakturaWierszCtrl
public class JpkFakturaWierszCtrl
{
[XmlElement]
public decimal LiczbaWierszyFaktur { get; set; }
[XmlElement]
public decimal WartoscWierszyFaktur { get; set; }
}
Class: JpkNaglowek
public class JpkNaglowek
{
private string dataOd = string.Empty;
private string dataDo = string.Empty;
[XmlIgnore]
public DateTime DataDoDt { get; private set; } //value of DataDo as DateTime
[XmlIgnore]
public DateTime DataOdDt { get; private set; } //value of DataDo as DateTime
[XmlElement(ElementName = "KodFormularza")]
public JpkNaglowekKodFormularza KodFormularza { get; set; } = new JpkNaglowekKodFormularza(); //FormCode
[XmlElement(ElementName = "WariantFormularza")]
public string WariantFormularza { get; set; } //Variant
[XmlElement(ElementName = "CelZlozenia")]
public int CelZlozenia { get; set; } //Purpose
[XmlElement(ElementName = "DataWytworzeniaJPK")]
public DateTime DataWytworzeniaJPK { get; set; } //CreationDate - DateTime
//DateFrom
[XmlElement(ElementName = "DataOd")]
public string DataOd
{
get
{
return this.dataOd;
}
set
{
this.dataOd = value;
//try to convert to DateTime
DateTime dt = DateTime.MinValue;
DateTime.TryParseExact(value, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dt);
//set value
this.DataOdDt = dt;
}
}
//DateTo
[XmlElement(ElementName = "DataDo")]
public string DataDo
{
get
{
return this.dataDo;
}
set
{
this.dataDo = value;
//try to convert to DateTime
DateTime dt = DateTime.MinValue;
DateTime.TryParseExact(value, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dt);
//set value
this.DataDoDt = dt;
}
}
[XmlElement(ElementName = "KodUrzedu")]
public string KodUrzedu { get; set; }
}
Note: In the code above you'll notice properties: DataDoDt and DataOdDt. They are public properties that hold the value of DataDo and DataOd respectively, as DateTime. By specifying [XmlIgnore] this property will be ignored during deserialization/serialization. In the XML, the format of the data is yyyy-MM-dd (ex: 2021-05-01), which is why it's necessary to specify the data type as a string. If the data type is specified as DateTime, when serialized the data in the XML file would look like yyyy-MM-ddTHH:mm:ss (ex: 2021-05-01T00:00:00)
Class: JpkNaglowekKodFormularza
public class JpkNaglowekKodFormularza
{
[XmlAttribute(AttributeName = "kodSystemowy")]
public string kodSystemowy { get; set; }
[XmlAttribute(AttributeName = "wersjaSchemy")]
public string wersjaSchemy { get; set; }
[XmlText]
public string Value { get; set; }
}
Class: JpkPodmiot1
public class JpkPodmiot1
{
[XmlElement(ElementName = "IdentyfikatorPodmiotu")]
public JpkPodmiot1IdentyfikatorPodmiotu IdentyfikatorPodmiotu { get; set; } = new JpkPodmiot1IdentyfikatorPodmiotu(); //SubjectId
[XmlElement(ElementName = "AdresPodmiotu")]
public JpkPodmiot1AdresPodmiotu AdresPodmiotu { get; set; } = new JpkPodmiot1AdresPodmiotu(); //Address
}
Class: JpkPodmiot1IdentyfikatorPodmiotu
[System.Xml.Serialization.XmlType(Namespace = "http://crd.gov.pl/xml/schematy/dziedzinowe/mf/2018/08/24/eD/DefinicjeTypy/")]
public class JpkPodmiot1IdentyfikatorPodmiotu
{
[XmlElement(ElementName = "NIP")]
public string NIP { get; set; }
[XmlElement(ElementName = "PelnaNazwa")]
public string PelnaNazwa { get; set; } //FullName
}
In the code above, notice that the namespace was specified.
Class: JpkPodmiot1AdresPodmiotu
[System.Xml.Serialization.XmlType(Namespace = "http://crd.gov.pl/xml/schematy/dziedzinowe/mf/2018/08/24/eD/DefinicjeTypy/")]
public class JpkPodmiot1AdresPodmiotu
{
[XmlElement(ElementName = "Wojewodztwo")]
public string Wojewodztwo { get; set; } //Province
[XmlElement(ElementName = "KodKraju")]
public string KodKraju { get; set; } //CountryCode
[XmlElement(ElementName = "Powiat")]
public string Powiat { get; set; } //District
[XmlElement(ElementName = "Gmina")]
public string Gmina { get; set; } //Community
[XmlElement(ElementName = "Ulica")]
public string Ulica { get; set; } //StreetName
[XmlElement(ElementName = "NrDomu")]
public Int32 NrDomu { get; set; } //HouseNumber
[XmlElement(ElementName = "NrLokalu")]
public Int32 NrLokalu { get; set; } //FlatNumber
[XmlElement(ElementName = "Miejscowosc")]
public string Miejscowosc { get; set; } //City
[XmlElement(ElementName = "KodPocztowy")]
public string KodPocztowy { get; set; } //PostalCode
}
In the code above, notice that the namespace was specified.
Here's a method that can be used to populate an instance of Jpk with some test data -- it's the data from the XML file that you specified above. It's useful for testing XML serialization.
CreateTestData
private Jpk CreateTestData()
{
Jpk jpk = new Jpk();
//Naglowek
jpk.Naglowek.KodFormularza.kodSystemowy = "SomeCode";
jpk.Naglowek.KodFormularza.wersjaSchemy = "1-0";
jpk.Naglowek.KodFormularza.Value = "SomeCode";
jpk.Naglowek.WariantFormularza = "3";
jpk.Naglowek.CelZlozenia = 1;
jpk.Naglowek.DataWytworzeniaJPK = DateTime.ParseExact("2021-06-30T15:57:53", "yyyy-MM-ddTHH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); //"2021-06-30T15:57:53"; //ToDo: change to DateTime
jpk.Naglowek.DataOd = "2021-05-01";
jpk.Naglowek.DataDo = "2021-05-31";
jpk.Naglowek.KodUrzedu = "0000";
//Podmiot1
jpk.Podmiot1.IdentyfikatorPodmiotu.NIP = "111111";
jpk.Podmiot1.IdentyfikatorPodmiotu.PelnaNazwa = "SomeName";
jpk.Podmiot1.AdresPodmiotu.Wojewodztwo = "voivodeship";
jpk.Podmiot1.AdresPodmiotu.KodKraju = "PL";
jpk.Podmiot1.AdresPodmiotu.Powiat = "Danzig";
jpk.Podmiot1.AdresPodmiotu.Gmina = "Danzig";
jpk.Podmiot1.AdresPodmiotu.Ulica = "SomeStreet";
jpk.Podmiot1.AdresPodmiotu.NrDomu = 81;
jpk.Podmiot1.AdresPodmiotu.NrLokalu = 1;
jpk.Podmiot1.AdresPodmiotu.Miejscowosc = "Danzig";
jpk.Podmiot1.AdresPodmiotu.KodPocztowy = "10-101";
//Faktura
JpkFaktura jpkFaktura = new JpkFaktura();
jpkFaktura.KodWaluty = "PLN";
jpkFaktura.P_1 = "2021-05-04";
jpkFaktura.P_2A = "11 / 1111";
jpkFaktura.P_3A = "Some Company";
jpkFaktura.P_3B = "Some Address";
jpkFaktura.P_3C = "Some Name";
jpkFaktura.P_3D = "Some Other Address";
jpkFaktura.P_4B = "Phone1";
jpkFaktura.P_5B = "Phone2";
jpkFaktura.P_13_1 = 1.00m; //need to use 'm' for decimal number
jpkFaktura.P_14_1 = 1.25m; //need to use 'm' for decimal number
jpkFaktura.P_15 = 0m; //need to use 'm' for decimal number
jpkFaktura.P_16 = false;
jpkFaktura.P_17 = false;
jpkFaktura.P_18 = false;
jpkFaktura.P_18A = false;
jpkFaktura.P_19 = false;
jpkFaktura.P_20 = false;
jpkFaktura.P_21 = false;
jpkFaktura.P_22 = false;
jpkFaktura.P_23 = false;
jpkFaktura.P_106E_2 = false;
jpkFaktura.P_106E_3 = false;
jpkFaktura.RodzajFaktury = "InvoiceType";
//add
jpk.Faktura.Add(jpkFaktura);
//FakturaCtrl
JpkFakturaCtrl jpkFakturaCtrl = new JpkFakturaCtrl();
jpkFakturaCtrl.LiczbaFaktur = 1m; //need to use 'm' for decimal number
jpkFakturaCtrl.WartoscFaktur = 2.00m; //need to use 'm' for decimal number
//add
jpk.FakturaCtrl.Add(jpkFakturaCtrl);
//FakturaWiersz
JpkFakturaWiersz jpkFakturaWiersz = new JpkFakturaWiersz();
jpkFakturaWiersz.P_2B = "04/123";
jpkFakturaWiersz.P_7 = "Text";
jpkFakturaWiersz.P_8B = 1.000000m; //need to use 'm' for decimal number
jpkFakturaWiersz.P_9A = 7.00m; //need to use 'm' for decimal number
jpkFakturaWiersz.P_11 = 7.00m; //need to use 'm' for decimal number
jpkFakturaWiersz.P_12 = 11;
//add
jpk.FakturaWiersz.Add(jpkFakturaWiersz);
//FakturaWierszCtrl
JpkFakturaWierszCtrl jpkFakturaWierszCtrl = new JpkFakturaWierszCtrl();
jpkFakturaWierszCtrl.LiczbaWierszyFaktur = 11m; //need to use 'm' for decimal number
jpkFakturaWierszCtrl.WartoscWierszyFaktur = 11.2m; //need to use 'm' for decimal number
//add
jpk.FakturaWierszCtrl.Add(jpkFakturaWierszCtrl);
return jpk;
}
Usage:
Jpk jpk1 = CreateTestData();
For XML serialization, use the following:
public static void SerializeObjectToXMLFile(object obj, string xmlFilename)
{
try
{
if (string.IsNullOrEmpty(xmlFilename))
{
return;
}//if
using (System.IO.TextWriter xmlStream = new System.IO.StreamWriter(xmlFilename))
{
//specify namespaces
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add(string.Empty, "http://jpk.mf.gov.pl/wzor/2019/09/27/09271/");
ns.Add("etd", "http://crd.gov.pl/xml/schematy/dziedzinowe/mf/2018/08/24/eD/DefinicjeTypy/");
//create new instance
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
//write XML to file
serializer.Serialize(xmlStream, obj, ns);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
throw ex;
}
}
Usage:
Jpk jpk1 = CreateTestData();
SerializeObjectToXMLFile(jpk1, #"C:\Temp\Test.xml");
For deserialization, use the following code:
public static T DeserializeXMLFileToObject<T>(string xmlFilename)
{
T rObject = default(T);
try
{
if (string.IsNullOrEmpty(xmlFilename))
{
return default(T);
}
using (System.IO.StreamReader xmlStream = new System.IO.StreamReader(xmlFilename))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
rObject = (T)serializer.Deserialize(xmlStream);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
throw ex;
}
return rObject;
}
Usage:
Jpk jpk1 = DeserializeXMLFileToObject<Jpk>(#"C:\Temp\Test.xml");
I am working with a third party XML API, that returns XML responses. With the API, I am trying to import orders. So, two scenario could occur when I import order with this API:
Success - e.g. xml response: http://pastebin.com/ZcvvYMX6
Failure - e.g. xml response: http://pastebin.com/iVjqMKAR
What I am working on is a wrapper for this API and I have a method called ImportOrders. Within this method, I am trying to deserialize the xml to an object so that I can return a common response.
For example; I will have a common response DTO class like this:
public class ImportOrderResponse
{
bool IsError { get; set; }
string ErrorMsg { get; set; }
string Result { get; set; }
}
and the ImportOrders method will return response conditionally like this:
if (apiResult.Contains("importFailures"))
{
// todo: deserialize
return new ImportOrderResponse()
{
IsError = true,
ErrorMsg = "todo - get failureMessage"
};
}
else
{
// todo: deserialize
return new ImportOrderResponse()
{
IsError = false,
Result = "todo - haven't worked out what i need to return"
};
}
Using this site http://xmltocsharp.azurewebsites.net I've generated the classes used for deserialization like this:
ImportSuccess.cs
[XmlRoot(ElementName = "import")]
public class ImportSuccess
{
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlAttribute(AttributeName = "operation")]
public string Operation { get; set; }
[XmlAttribute(AttributeName = "entity")]
public string Entity { get; set; }
[XmlAttribute(AttributeName = "externalReference")]
public string ExternalReference { get; set; }
[XmlAttribute(AttributeName = "item")]
public string Item { get; set; }
[XmlAttribute(AttributeName = "queryTime")]
public string QueryTime { get; set; }
}
[XmlRoot(ElementName = "importSuccesses")]
public class ImportSuccesses
{
[XmlElement(ElementName = "import")]
public List<ImportSuccess> Import { get; set; }
}
[XmlRoot(ElementName = "importResult")]
public class ImportResult
{
[XmlElement(ElementName = "importSuccesses")]
public ImportSuccesses ImportSuccesses { get; set; }
[XmlElement(ElementName = "importFailures")]
public string ImportFailures { get; set; }
[XmlElement(ElementName = "importDuplicates")]
public string ImportDuplicates { get; set; }
}
ImportFailure.cs
[XmlRoot(ElementName = "import")]
public class ImportFailure
{
[XmlElement(ElementName = "failureMessage")]
public string FailureMessage { get; set; }
[XmlElement(ElementName = "failureDetail")]
public string FailureDetail { get; set; }
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlAttribute(AttributeName = "operation")]
public string Operation { get; set; }
[XmlAttribute(AttributeName = "externalReference")]
public string ExternalReference { get; set; }
[XmlAttribute(AttributeName = "queryTime")]
public string QueryTime { get; set; }
}
[XmlRoot(ElementName = "importFailures")]
public class ImportFailures
{
[XmlElement(ElementName = "import")]
public ImportFailure Import { get; set; }
}
[XmlRoot(ElementName = "importResult")]
public class ImportResult
{
[XmlElement(ElementName = "importSuccesses")]
public string ImportSuccesses { get; set; }
[XmlElement(ElementName = "importFailures")]
public ImportFailures ImportFailures { get; set; }
[XmlElement(ElementName = "importDuplicates")]
public string ImportDuplicates { get; set; }
}
But now I have a problem. Both the success & failure response contains the node importResult but has varying fields/element names.
Now I have an error because I have a duplicate ImportResult class.
How can I solve this? What is the correct way to handle this scenario?
I believe I was being silly... I was going about this with a wrong approach.
What I did was;
Call api with correct data for a success response, took the xml and created a class
call api with wrong/bad data for a failure response, took the xml and tried to create another class
After some digging around, I realised that the response structure is very close for success/failure.
So, I called the api and instead of sending one order; I sent 3 orders for 3 specific scenario. This resulted in a single XML response like this:
http://pastebin.com/sNgWAwq1
Now I have a good structure of the XML for (Success/Failure & Duplicates). Using this XML, I've generated the following class (for deserialisation purpose):
[XmlRoot(ElementName = "import")]
public class Import
{
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlAttribute(AttributeName = "operation")]
public string Operation { get; set; }
[XmlAttribute(AttributeName = "entity")]
public string Entity { get; set; }
[XmlAttribute(AttributeName = "externalReference")]
public string ExternalReference { get; set; }
[XmlAttribute(AttributeName = "item")]
public string Item { get; set; }
[XmlAttribute(AttributeName = "queryTime")]
public string QueryTime { get; set; }
[XmlElement(ElementName = "failureMessage")]
public string FailureMessage { get; set; }
[XmlElement(ElementName = "failureDetail")]
public string FailureDetail { get; set; }
[XmlElement(ElementName = "duplicateMessage")]
public string DuplicateMessage { get; set; }
[XmlElement(ElementName = "duplicateDetail")]
public string DuplicateDetail { get; set; }
}
[XmlRoot(ElementName = "importSuccesses")]
public class ImportSuccesses
{
[XmlElement(ElementName = "import")]
public List<Import> Import { get; set; }
}
[XmlRoot(ElementName = "importFailures")]
public class ImportFailures
{
[XmlElement(ElementName = "import")]
public Import Import { get; set; }
}
[XmlRoot(ElementName = "importDuplicates")]
public class ImportDuplicates
{
[XmlElement(ElementName = "import")]
public Import Import { get; set; }
}
[XmlRoot(ElementName = "importResult")]
public class OrderImportResponse
{
[XmlElement(ElementName = "importSuccesses")]
public ImportSuccesses ImportSuccesses { get; set; }
[XmlElement(ElementName = "importFailures")]
public ImportFailures ImportFailures { get; set; }
[XmlElement(ElementName = "importDuplicates")]
public ImportDuplicates ImportDuplicates { get; set; }
}
Now, this can work for me. I can call the api and deserialise the response to the OrderImportResponse class (without checking what kind of xml response it was) and it will have all three scenario responses I am looking for.
I'm trying to deserialize a xml string to a c# object. This is the message:
<message from='test1#localhost' to='test2#localhost'><result xmlns='urn:xmpp:mam:tmp' id='A6QV1I4TKO81'><forwarded xmlns='urn:xmpp:forward:0'><delay xmlns='urn:xmpp:delay' from='test1#localhost' stamp='2015-07-21T09:12:09Z'></delay><message type='mchat'><subject/><body/></message></forwarded></result></message>
And this is the class
public class Delay {
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
[XmlAttribute(AttributeName="from")]
public string From { get; set; }
[XmlAttribute(AttributeName="stamp")]
public string Stamp { get; set; }
}
public class Active {
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
}
public class XmppMessage {
[XmlElement(ElementName="body")]
public string Body { get; set; }
[XmlAttribute(AttributeName="lang")]
public string Lang { get; set; }
[XmlAttribute(AttributeName="type")]
public string Type { get; set; }
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
[XmlAttribute(AttributeName="to")]
public string To { get; set; }
}
public class Forwarded {
[XmlElement(ElementName="delay")]
public Delay Delay { get; set; }
[XmlElement(ElementName="message")]
public XmppMessage Message { get; set; }
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
}
public class Result {
[XmlElement(ElementName="forwarded")]
public Forwarded Forwarded { get; set; }
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
}
[XmlRoot(ElementName="message")]
public class MessageHistory {
[XmlElement(ElementName="result")]
public Result Result { get; set; }
[XmlAttribute(AttributeName="from")]
public string From { get; set; }
[XmlAttribute(AttributeName="to")]
public string To { get; set; }
}
This is the code to deserialise:
MessageHistory messageNode;
XmlSerializer serializer = new XmlSerializer(typeof(MessageHistory));
using (StringReader reader = new StringReader(message))
{
messageNode = (MessageHistory)(serializer.Deserialize(reader));
}
The object property "from" and "to" are fine but the "Result" is returning null. I can't understand what I'm missing here...
The problem is the namespaces in the XML. you have to specify the namespaces explicitly, like this:
public class Forwarded
{
[XmlElement(ElementName = "delay", Namespace = "urn:xmpp:delay")]
public Delay Delay { get; set; }
[XmlElement(ElementName = "message")]
public XmppMessage Message { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
}
public class Result
{
[XmlElement(ElementName = "forwarded", Namespace = "urn:xmpp:forward:0")]
public Forwarded Forwarded { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "message")]
public class MessageHistory
{
[XmlElement(ElementName = "result", Namespace = "urn:xmpp:mam:tmp")]
public Result Result { get; set; }
[XmlAttribute(AttributeName = "from")]
public string From { get; set; }
[XmlAttribute(AttributeName = "to")]
public string To { get; set; }
}
I can't understand why object is null:
WebClient browse = new WebClient();
StreamReader res = new StreamReader(browse.OpenRead("http://ws.audioscrobbler.com/2.0/?method=track.getinfo&api_key=b25b959554ed76058ac220b7b2e0a026&artist=cher&track=believe"));
string result = res.ReadToEnd();
XmlDocument xmltrackinfo = new XmlDocument();
xmltrackinfo.InnerXml = result;
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lfm";
xRoot.IsNullable = true;
XmlSerializer xs = new XmlSerializer(typeof(fm), xRoot);
fm rez = (fm) xs.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(result)));
Object Model:
[Serializable()]
[XmlRoot(ElementName = "lfm", IsNullable = true)]
public class fm
{
[XmlElement("lfm")]
public Track lfm { get; set; }
}
[Serializable()]
[XmlRoot(ElementName = "artist", IsNullable = true)]
public class artist
{
public string name { get; set; }
public string mbid { get; set; }
public string url { get; set; }
}
[Serializable()]
[XmlRoot(ElementName = "album", IsNullable = true)]
public class album
{
public string artist { get; set; }
public string title { get; set; }
public string mbid { get; set; }
public string url { get; set; }
public List<string> image { get; set; }
}
[Serializable()]
[XmlRoot(ElementName = "tag", IsNullable = true)]
public class tag
{
public string name { get; set; }
public string url { get; set; }
}
[Serializable()]
[XmlRoot(ElementName = "wiki", IsNullable = true)]
public class wiki
{
public string summary { get; set; }
public string content { get; set; }
}
[Serializable()]
[XmlRoot(ElementName = "track", IsNullable = true)]
public class Track
{
public string id { get; set; }
public string name { get; set; }
public string mbid { get; set; }
public string url { get; set; }
public string duration { get; set; }
public string streamable { get; set; }
public string listeners { get; set; }
public string playcount { get; set; }
public artist artist { get; set; }
public album album { get; set; }
public List<tag> toptags { get; set; }
public wiki wiki { get; set; }
}
and XML:
http://ws.audioscrobbler.com/2.0/?method=track.getinfo&api_key=b25b959554ed76058ac220b7b2e0a026&artist=cher&track=believe
so what should I do?
Try renaming your fm class to lfm.
public class lfm
{
public Track track { get; set; }
}
and then you could also get rid of the xRoot variable:
XmlSerializer xs = new XmlSerializer(typeof(lfm));
lfm rez = (lfm) xs.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(result)));
Also you don't need the [Serializable] attribute. This is used for binary serialization and is completely ignored by the XmlSerializer class.
The lfm property of the fm class must have track as its XmlElement:
[Serializable()]
[XmlRoot(ElementName = "lfm", IsNullable = true)]
public class fm
{
[XmlElement("track")]
public Track lfm { get; set; }
}