After read multiples posts related on this error and not found an solution to my problem, I explain it here.
I use XmlSerializer to serialize simple classes.
Here's my code:
private void btnGenerateXml_Click(object sender, RoutedEventArgs e)
{
Orchard orchard = new Orchard
{
Recipe = new Recipe
{
Name = "Generated by JooWeb.Tools",
Author = "admin",
ExportUtc = DateTime.UtcNow
},
MyDatas = new MyDatas
{
//Test = "test"
TrendDatas = new TrendDatas
{
Id = null,
Status = "Published",
TrendDatasPart = new TrendDatasPart
{
IdSource = 0,
PostalCode = "1000",
Locality = "Test5",
Surface = (decimal)0.00,
Price = (decimal)0.00,
Type = "",
InsertDateIndicator = "",
UpdateDateIndicator = "",
GetFromDate = DateTime.Now,
UpdatedDate = new DateTime(1900, 1, 1)
},
CommonPart = new CommonPart
{
Owner = "/User.UserName=admin",
CreatedUtc = DateTime.UtcNow,
PublishedUtc = DateTime.UtcNow,
ModifiedUtc = DateTime.UtcNow
}
}
}
};
XmlSerializer orchardXmlSerializer = new XmlSerializer(typeof(Orchard));
var path = #"C:\Temp\orchardFileImport_" + string.Format("{0:yyyyMMdd}", DateTime.Today) + ".xml";
if (File.Exists(path))
File.Delete(path);
orchardXmlSerializer.Serialize(File.OpenWrite(path), orchard);
MessageBox.Show("Finished");
}
}
[XmlRoot]
public class Orchard
{
[XmlElement]
public Recipe Recipe { get; set; }
[XmlElement(ElementName = "Data")]
public MyDatas MyDatas { get; set; }
}
public class Recipe
{
[XmlElement]
public string Name { get; set; }
[XmlElement]
public string Author { get; set; }
[XmlElement]
public DateTime ExportUtc { get; set; }
}
public class MyDatas
{
public MyDatas()
{
}
//[XmlElement]
//public string Test { get; set; }
[XmlElement]
public TrendDatas TrendDatas { get; set; }
}
public class TrendDatas
{
[XmlAttribute]
public string Status { get; set; }
[XmlAttribute]
public int? Id { get; set; }
//[XmlIgnore]
[XmlElement]
public TrendDatasPart TrendDatasPart { get; set; }
//[XmlIgnore]
[XmlElement]
public CommonPart CommonPart { get; set; }
}
public class TrendDatasPart
{
[XmlAttribute]
public int IdSource { get; set; }
[XmlAttribute]
public string PostalCode { get; set; }
[XmlAttribute]
public string Locality { get; set; }
[XmlAttribute]
public decimal Surface { get; set; }
[XmlAttribute]
public decimal Price { get; set; }
[XmlAttribute]
public string Type { get; set; }
[XmlAttribute]
public string InsertDateIndicator { get; set; }
[XmlAttribute]
public string UpdateDateIndicator { get; set; }
[XmlAttribute]
public DateTime GetFromDate { get; set; }
[XmlAttribute]
public DateTime UpdatedDate { get; set; }
}
public class CommonPart
{
[XmlAttribute]
public string Owner { get; set; }
[XmlAttribute]
public DateTime CreatedUtc { get; set; }
[XmlAttribute]
public DateTime PublishedUtc { get; set; }
[XmlAttribute]
public DateTime ModifiedUtc { get; set; }
}
With this code when I click on Generate xml file, I got the error InvalidOperationException
There was an error reflecting type 'MergeExcelFiles.Orchard'.
{"There was an error reflecting property 'MyDatas'."}
Like you see in my comments, I try to just add a string xmlElement to node MyDatas, with this change I got no error but in the xml file I don't have any node with name Data.
I don't understand why with class Recipe all look right but with node MyDatas nothing showed in xml file or got this error "InvalidOperationException".
You need to dig into your error message more because the reason is in the innermost exception:
System.InvalidOperationException: Cannot serialize member 'Id' of type System.Nullable`1[System.Int32]. XmlAttribute/XmlText cannot be used to encode complex types.
The issue is that you have a nullable value type as a property (TrendDatas.Id) to be serialized as an attribute and XmlSerializer does not handle these well. There are a number of workarounds listed here and here. None of them is particularly elegant. The best option might be changing the definition of Id to an element:
public class TrendDatas
{
// ... snip ...
[XmlElement(IsNullable = true)]
public int? Id { get; set; }
public bool ShouldSerializeId() { return Id.HasValue; }
// ... snip ...
}
The ShouldSerializeId is a method that, by convention, the serializer uses to decide if the property should be serialized in the output. In the case of a null value, no element will be defined in the serialized output.
Related
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");
Json.net doesn't deserialize JSON it previously created, if there is a collection of generic types. How can I deserialize such JSON correctly?
Here is the JSON I am trying to deserialize:
{
"$type":"MyProject.Messages.ChangeMsg`1[[MyProject.Classes.DTO.DeviceDTO, MyProject]], MyProject",
"ChangedDataList":{
"$type":"System.Collections.Generic.List`1[[System.Tuple`2[[MyProject.Classes.DTO.DeviceDTO, MyProject],[MyProject.Enums.ChangedStatus, MyProject]], mscorlib]], mscorlib",
"$values":[
{
"$type":"System.Tuple`2[[MyProject.Classes.DTO.DeviceDTO, MyProject],[MyProject.Enums.ChangedStatus, MyProject]], mscorlib",
"Item1":{
"$type":"MyProject.Classes.DTO.SwitchDeviceDTO, MyProject",
"Id":318,
"Name":"Device",
"Ios":{
"$type":"System.Collections.Generic.List`1[[MyProject.Classes.DTO.IoDTO, MyProject]], mscorlib",
"$values":[
]
},
"GuiProperties":{
"$type":"System.Collections.Generic.List`1[[MyProject.Classes.DTO.GuiPropertiesDTO, MyProject]], mscorlib",
"$values":[
{
"$type":"MyProject.Classes.DTO.GuiPropertiesDTO, MyProject",
"Id":319,
"X":200,
"Y":0,
"DeviceId":318,
"ChangedStatus":0
}
]
},
"ChangedStatus":0
},
"Item2":0
}
]
}
}
I can't see any error in the output-window, but after deserialization ChangedDataList is always null.
Here the deserialization code:
private static T GetMessage<T>(string msg)
{
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
return JsonConvert.DeserializeObject<T>(msg, settings);
}
And here is a full LinqPad Example:
void Main()
{
ChangeMsg<DeviceDTO> chgMsg = new ChangeMsg<DeviceDTO>(new List<Tuple<DeviceDTO, ChangedStatus>>() { new Tuple<DeviceDTO, ChangedStatus>(new DeviceDTO() { Id = 318, Name = "Device" }, ChangedStatus.New)});
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
string msg = JsonConvert.SerializeObject(chgMsg, settings);
var obj = JsonConvert.DeserializeObject(msg, settings);
}
// Define other methods and classes here
public enum ChangedStatus
{
New,
Modified,
Deleted
}
[DataContract]
public enum ChangedStatusDTO
{
[EnumMember]
New,
[EnumMember]
Modified,
[EnumMember]
Deleted
}
public interface IChangedStatusDTO
{
ChangedStatusDTO ChangedStatus { get; set; }
}
public abstract class SdnMessage
{
public int RequestId { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ChangeMsg<T> : SdnMessage where T : IChangedStatusDTO
{
[JsonConstructor]
public ChangeMsg(List<Tuple<T, ChangedStatus>> tuples)
{
ChangedDataList = tuples;
}
[JsonProperty("ChangedDataList")]
public List<Tuple<T, ChangedStatus>> ChangedDataList { get; }
}
[DataContract(IsReference = true)]
public class ConnectionDTO : IChangedStatusDTO
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int FromId { get; set; }
[DataMember]
public int? FromDeviceId { get; set; }
[DataMember]
public int ToId { get; set; }
[DataMember]
public int? ToDeviceId { get; set; }
[DataMember]
public ChangedStatusDTO ChangedStatus { get; set; }
}
[DataContract]
public class GuiPropertiesDTO : IChangedStatusDTO
{
[DataMember]
public int Id { get; set; }
[DataMember]
public int X { get; set; }
[DataMember]
public int Y { get; set; }
[DataMember]
public int Z { get; set; }
[DataMember]
public int Width { get; set; }
[DataMember]
public int Height { get; set; }
[DataMember]
public int? DeviceId { get; set; }
[DataMember]
public ChangedStatusDTO ChangedStatus { get; set; }
}
[DataContract(IsReference = true)]
public class IoDTO : IChangedStatusDTO
{
[DataMember]
public int Id { get; set; }
[DataMember]
public int Number { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public int? DeviceId { get; set; }
[DataMember]
public virtual ICollection<ConnectionDTO> ConnectionFroms { get; set; }
[DataMember]
public virtual ICollection<ConnectionDTO> ConnectionTos { get; set; }
[DataMember]
public ChangedStatusDTO ChangedStatus { get; set; }
}
[DataContract(IsReference = true)]
public class DeviceDTO : IChangedStatusDTO
{
public DeviceDTO()
{
Name = string.Empty;
Ios = new HashSet<IoDTO>();
GuiProperties = new HashSet<GuiPropertiesDTO>();
}
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int ManufacturerId { get; set; }
[DataMember]
public virtual ICollection<IoDTO> Ios { get; set; }
[DataMember]
public virtual ICollection<GuiPropertiesDTO> GuiProperties { get; set; }
[DataMember]
public ChangedStatusDTO ChangedStatus { get; set; }
}
Your problem is with the ChangeMsg<T> constructor:
[JsonObject(MemberSerialization.OptIn)]
public class ChangeMsg<T> : SdnMessage where T : IChangedStatusDTO
{
[JsonConstructor]
public ChangeMsg(List<Tuple<T, ChangedStatus>> tuples)
{
ChangedDataList = tuples;
}
[JsonProperty("ChangedDataList")]
public List<Tuple<T, ChangedStatus>> ChangedDataList { get; }
}
The name of the constructor's argument is tuples, which is not the same as the property name "ChangedDataList". Thus when deserializing JSON with a "ChangedDataList" property as shown in the question, Json.NET has no way to know that it should be bound to the tuples argument. This is because Json.NET will bind JSON properties to constructor arguments by matching their names, modulo case. Instead null is passed in and the ChangedDataList c# property is never allocated. As this property is get-only, Json.NET is subsequently unable to populate it, and the values from the JSON are skipped.
To solve the problem, you can change the name of the constructor argument to be consistent with the property name. You should also either allocate an empty list or throw a ArgumentNullException if null is passed in:
[JsonConstructor]
public ChangeMsg(List<Tuple<T, ChangedStatus>> changedDataList)
{
this.ChangedDataList = changedDataList ?? new List<Tuple<T, ChangedStatus>>();
}
(My preference is not to throw ArgumentNullException from deserialization code, but your preferences may differ.)
Alternatively, if you are think it is too fragile that serialization depends on the naming of constructor arguments, you can explicitly mark the constructor argument with [JsonProperty] like so:
[JsonConstructor]
public ChangeMsg( [JsonProperty("ChangedDataList")] List<Tuple<T, ChangedStatus>> tuples)
{
this.ChangedDataList = tuples ?? new List<Tuple<T, ChangedStatus>>();
}
With C#, I try without success to de-serialize this json content :
{
"code":200,
"message":"OK",
"name":"The name",
"description":"The description",
"tags":{
"0.1.3":{
"date":"2015-03-11",
"author":"SOMEONE",
},
"0.1.2":{
"date":"2015-03-11",
"author":"SOMEONE",
}
}
}
You have noticed, there's a list of "tag" objects, but I have not a table.
Beginning of the target classes :
[DataContract]
public class Project
{
[DataMember]
public int code { get; set; }
[DataMember]
public string message { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public string description { get; set; }
[DataMember]
**How can I handle tags entries ?**
}
[DataContract]
public class Tag
{
[DataMember]
public string date { get; set; }
[DataMember]
public string author { get; set; }
}
If you're using JSON.NET, then you can have the following:
[DataContract]
public class Project
{
[DataMember]
public int code { get; set; }
[DataMember]
public string message { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public string description { get; set; }
[DataMember]
public Dictionary<string, Tag> tags { get; set; }
}
[DataContract]
public class Tag
{
[DataMember]
public string date { get; set; }
[DataMember]
public string author { get; set; }
}
Which then you would use the following way (assuming responseString contains your JSON):
Project project = JsonConvert.DeserializeObject<Project>(responseString);
foreach (KeyValuePair<string, Tag> tag in project.tags)
{
Debug.WriteLine("Version: {0}", tag.Key);
Debug.WriteLine("Date: {0}", tag.Value.date);
Debug.WriteLine("Author: {0}", tag.Value.author);
}
The JSON input is valid according to RFC 4627 (JSON specfication).
http://www.freeformatter.com/json-validator.html
JSON sturcture is based on Key Value pair. Correct JSON format is like:
{
"object":{
"DataMember1":"String content",
"DataMember2":"String content",
"DataMember3":"String content"
},
"object2":{
"DataMember1":"String content",
"DataMember2":"String content"
}
}
Goto basics
To check your json structure you can validate from click here
Hmm, I was able to deserialize:
Newtonsoft.Json.JsonConvert.DeserializeObject<Project>(json);
However, here is my class definition:
public class Project
{
public string code { get; set; }
public string message { get; set; }
public string name { get; set; }
public string description { get; set; }
public Dictionary<string,Tag> tags { get; set; }
}
public class Tag
{
public string date { get; set; }
public string author { get; set; }
}
Thanks to Arturo Torres Sánchez. To get the "tag" entries, the declaration must be :
[DataContract]
public class Project
{
[DataMember]
public int code { get; set; }
[DataMember]
public string message { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public string description { get; set; }
[DataMember]
public Dictionary<string, Tag> tags { get; set; }
}
And the most important, I must modify the default settings and use the new settings when I create the instance of DataContractJsonSerializer.
DataContractJsonSerializerSettings settings =
new DataContractJsonSerializerSettings();
settings.UseSimpleDictionaryFormat = true;
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(typeof(Project), settings);
Project results = (Project)serializer.ReadObject(ms);
Without the settings.UseSimpleDictionaryFormat = true; tag's object count is 0.
I have this model:
[XmlArray(ElementName = "Listing")]
[XmlArrayItem(ElementName = "Classification")]
public List<Classification> classifications { get; set; }
[XmlRoot("Listing")]
public class Classification
{
[XmlAttribute("Name")]
public string name { get; set; }
[XmlText]
public string Value { get; set; }
}
That gives me this:
<Listing>
<Classification Name="Location">AsiaPacific</Classification>
</Listing>
How should I modify my class to get this:
<Listing reference = "MyReference">
<Classification Name="Location">AsiaPacific</Classification>
</Listing>
After a few (hundreds) trial and error, I got the result that I need by modifying the model to:
[XmlElement(ElementName = "Listing")]
public ClassificationWrapper classificationWrapper { get; set; }
public class ClassificationWrapper
{
[XmlAttribute("reference")]
public string ref= "MyReference";
[XmlElement("Classification", typeof(Classification))]
public List<Classification> classifications { get; set; }
public ClassificationWrapper() { this.classifications = new List<Classification>(); }
}
public class Classification
{
[XmlAttribute("Name")]
public string name { get; set; }
[XmlText]
public string Value { get; set; }
}
I have the following JSON object coming to me from a web service
{
"room":{
"name":"Thunderdome",
"created_at":"2012/04/15 00:36:27 +0000",
"id":xxxxxxx,
"users":[
{
"type":"Member",
"avatar_url":"url",
"created_at":"2012/02/27 14:11:57 +0000",
"id":1139474,
"email_address":"xxx#xxxxxxx.com",
"admin":false,
"name":"xxxx xxxxx"
},
{
"type":"Member",
etc
I'm using the following line to deserialize:
var room = JsonConvert.DeserializeObject<SingleRoom>(text);
And the following mapping classes
public class SingleRoom
{
public Room Room { get; set; }
}
[DataContract]
public class Room
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
public string Image {
get { return "Assets/campfire.png"; }
}
[DataMember]
public string Topic { get; set; }
[DataMember(Name ="membership_limit")]
public int MembershipLimit { get; set; }
[DataMember]
public bool Full { get; set; }
[DataMember]
public bool Locked { get; set; }
[DataMember(Name = "open_to_guests")]
public bool OpenToGuests { get; set; }
[DataMember(Name = "updated_at")]
public DateTime UpdatedAt { get; set; }
[DataMember(Name = "created_at")]
public DateTime CreatedAt { get; set; }
[DataMember(Name = "active_token_value")]
public string ActiveTokenValue { get; set; }
[DataMember(Name = "Users")]
public List<User> Users { get; set; }
}
[DataContract]
public class User
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember(Name = "email_address")]
public string EmailAddress { get; set; }
[DataMember]
public bool Admin { get; set; }
[DataMember]
public DateTime CreatedAt { get; set; }
[DataMember]
public string Type { get; set; }
[DataMember(Name = "avatar_url")]
public string AvatarUrl { get; set; }
}
The 'Room' object deserializes correctly but the Users property on the Room is always null. I know that a null result is the result of a JSON.NET serialization that couldn't find the matching property. However, I think I have it right? List should match to user. I know the array object users in the JSON doesn't have named children, is that the issue? If so, how do I solve it?
Thanks!
This works.... You can rename them (or use JsonProperty attribute) to use c# style property names
(BTW: Json.Net doesn't require DataMember, DataContract attributes)
var obj = JsonConvert.DeserializeObject<SingleRoom>(json)
public class User
{
public string type { get; set; }
public string avatar_url { get; set; }
public string email_address { get; set; }
public bool admin { get; set; }
public string name { get; set; }
public string created_at { get; set; }
public int id { get; set; }
}
public class Room
{
public string topic { get; set; }
public int membership_limit { get; set; }
public bool locked { get; set; }
public string name { get; set; }
public List<User> users { get; set; }
public bool full { get; set; }
public bool open_to_guests { get; set; }
public string updated_at { get; set; }
public string created_at { get; set; }
public int id { get; set; }
}
public class SingleRoom
{
public Room room { get; set; }
}
PS: You may find that site useful http://json2csharp.com/ .
In my case, I had missing to add the "public" key for the child property.