"Token StartElement in state Epilog would result in an invalid XML document" on InvoiceDescriptor.Save(stream, v21, Basic)
springy76 opened this issue · 6 comments
I just updated the NuGet package from 7.0.1 to 11.3.0 and a unit test (creating an InvoiceDescriptor from scratch) not having been touched for months (or years) now fails:
System.InvalidOperationException
HResult=0x80131509
Message=Token StartElement in state Epilog would result in an invalid XML document.
Source=System.Private.Xml
StackTrace:
at System.Xml.XmlTextWriter.AutoComplete(Token token)
at System.Xml.XmlTextWriter.WriteStartElement(String prefix, String localName, String ns)
at s2industries.ZUGFeRD.ProfileAwareXmlTextWriter.WriteStartElement(String prefix, String localName, String ns, Profile profile)
at s2industries.ZUGFeRD.ProfileAwareXmlTextWriter.WriteStartElement(String localName, Profile profile)
at s2industries.ZUGFeRD.InvoiceDescriptor21Writer.Save(InvoiceDescriptor descriptor, Stream stream)
at s2industries.ZUGFeRD.InvoiceDescriptor.Save(Stream stream, ZUGFeRDVersion version, Profile profile)
at UnitTest
This exception was originally thrown at this call stack:
System.Xml.XmlTextWriter.AutoComplete(System.Xml.XmlTextWriter.Token)
System.Xml.XmlTextWriter.WriteStartElement(string, string, string)
s2industries.ZUGFeRD.ProfileAwareXmlTextWriter.WriteStartElement(string, string, string, s2industries.ZUGFeRD.Profile)
s2industries.ZUGFeRD.ProfileAwareXmlTextWriter.WriteStartElement(string, s2industries.ZUGFeRD.Profile)
s2industries.ZUGFeRD.InvoiceDescriptor21Writer.Save(s2industries.ZUGFeRD.InvoiceDescriptor, System.IO.Stream)
s2industries.ZUGFeRD.InvoiceDescriptor.Save(System.IO.Stream, s2industries.ZUGFeRD.ZUGFeRDVersion, s2industries.ZUGFeRD.Profile)
UnitTest
Hi @springy76,
it is really hard to guess what might have gone wrong based on the exception. Can you please provide your unit test (working without any dependencies) and the file that is generated?
Since the surrounding code goes through several layers of abstraction and transformation code I can't provide a simple sample, but I serialized the final InvoiceDescriptor
to json. Note that TradeLineItems
does not get deserialized, but I guess this is something you can fix easily since everything else looks good so far.
var jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true, Converters = { new JsonStringEnumConverter() { } } };
var json = JsonSerializer.Serialize(invoiceDescriptor, jsonSerializerOptions);
var rebuilt = JsonSerializer.Deserialize<InvoiceDescriptor>(json, jsonSerializerOptions);
var json2 = JsonSerializer.Serialize(rebuilt, jsonSerializerOptions);
Debug.Assert(json2 == json);
I'm sorry, but generating the file from the data you provided works without any problem. Please try:
var jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true, Converters = { new JsonStringEnumConverter() { } } };
var json = File.ReadAllText("InvoiceDescriptorSerialized.json");
InvoiceDescriptor rebuilt = JsonSerializer.Deserialize<InvoiceDescriptor>(json, jsonSerializerOptions);
rebuilt.Save("output.xml", ZUGFeRDVersion.Version21, Profile.Basic);
As said, currently all TradeLineItems get stripped/ignored during Deserialize - and they seem to be the error source when they exist. If you add those 2 lines to your code you'll see:
var json2 = JsonSerializer.Serialize(rebuilt, jsonSerializerOptions);
Debug.Assert(json2 == json); // not equal, entire TradLineItems-Subtree/-Array is empty
The answer seems to be quite simple:
The internal setter of trade line items prohibts creating them by the deserializer:
ZUGFeRD-csharp/ZUGFeRD/InvoiceDescriptor.cs
Line 175 in 01e3c9b
Working alone in my spare time on the project, I am happy to accept your sponsoring:
https://github.com/sponsors/stephanstapel
The internal setter of trade line items prohibts creating them by the deserializer:
Klarer Fall von "Softwareproblem, kann man nichts machen" .
Irgendwie hatte ich die völlig irre Annahme, dass das auch in Bezug auf andere Bugreports - völlig losgelöst von diesem Issue - von Hilfe wäre, wenn man einen InvoiceDescriptor
nicht nur serialisieren, sondern auch verlustfrei deserialisieren kann.