XmlSerializer removes data when serializing - c#

I have a very annoying issue serializing a large XML object. I have defined a class FetchYearlyInvoiceStatusRequest:
[Serializable]
public class FetchYearlyInvoiceStatusRequest
{
[XmlArray("INVOICES")]
[XmlArrayItem("INVOICE", typeof(InvoiceRequest))]
public List<InvoiceRequest> Invoices;
}
[Serializable]
public class InvoiceRequest
{
[XmlElement("CONTRACTACCOUNT")]
public string ContractAccount;
[XmlElement("INVOICENUMBER")]
public string InvoiceNumber;
}
I have an instance of this class with around 700 items in the list. When I use this code to serialize this to XML:
XmlSerializer serializerRequest = new XmlSerializer(typeof(FetchYearlyInvoiceStatusRequest));
string invoices;
using (var sw = new StringWriter())
{
serializerRequest.Serialize(sw, odsrequest);
invoices = sw.ToString();
}
The invoices string now contains a long list of invoices, but the serializer (or the memorystream?) just cut off the middle half. So really in the middle of the output string, the literal text is:
<INVOICE>
<CONTRACTACCOUNT>3006698362</CONTRACTACCOUNT>
<INVOICENUMBER>40523461958</INVOICENUMBER>
</INVOICE>
<INVOICE>
<CONTRACTACCOUNT>3006362096</CONTRACTACCOUNT>
<INVOICENUMBER>40028149026</INVOICENUMBE... <CONTRACTACCOUNT>3006362096</CONTRACTACCOUNT>
<INVOICENUMBER>55002448279</INVOICENUMBER>
</INVOICE>
<INVOICE>
<CONTRACTACCOUNT>3006362096</CONTRACTACCOUNT>
<INVOICENUMBER>42514938204</INVOICENUMBER>
</INVOICE>
This is simply corrupt. When I write to a file using almost the same code (but using a StreamWriter isntead of StringWriter):
XmlSerializer serializerRequest = new XmlSerializer(typeof(FetchYearlyInvoiceStatusRequest));
string invoices;
using (var sw = new StreamWriter("c:\\temp\\output.xml"))
{
serializerRequest.Serialize(sw, odsrequest);
}
The output in the file is perfect (snippet at the same location as above where it cuts off):
<INVOICE>
<CONTRACTACCOUNT>3006362096</CONTRACTACCOUNT>
<INVOICENUMBER>40028149026</INVOICENUMBER>
</INVOICE>
<INVOICE>
<CONTRACTACCOUNT>3007728722</CONTRACTACCOUNT>
<INVOICENUMBER>40027928855</INVOICENUMBER>
</INVOICE>
Please someone tell me what happens. I've googled for "stringwriter limitations", "xmlserializer limitations and more. Nothing :(
Any help appreciated!

Related

Deserializing XML Array in unity

Im trying to deserialize an array of objects from a XML Document.
The document built in the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element>
.....
</element>
<element>
.....
</element>
</root>
But for some reason Im having lots of problems doing so.
This is my function which I call to deserialize it:
public static CardModel[] Load(string text)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "root";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(CardModel[]),xRoot);
StringReader reader = new StringReader(text);
CardModel[] o = serializer.Deserialize(reader) as CardModel[];
reader.Close();
return o;
}
And I am not sure if its done correctly or not. Because I know that in json you are unable to deserialize an array and you have to do some sort of "hack".
In the CardModel class (which is the element of the array) i use above the class the tag [XmlRoot("root")]. I have also tried to use [XmlRoot("element")] but still im getting stuck.
Afaik you can't directly deserialize into an array but would need a wrapper class like
[Serializable]
[XMLRoot("root")]
public class Root
{
// This does the magic of treating all "element" items nested under the root
// As part of this array
[XmlArray("element")]
public CardModel[] models;
}
And rather deserilialize into that like
public static CardModel[] Load(string text)
{
// I don't think that you need the attribute overwrite here
var serializer = new XmlSerializer(typeof(Root));
using(var reader = new StringReader(text))
{
var root = (Root) serializer.Deserialize(reader);
return root.models;
}
}

XML error during deserialize

I have problem when I'm trying to deserialize an XML to object. My XML look like:
<?xml version="1.0" encoding="utf-16"?>
<Products
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AllProducts>
<Product>
<ID>8</ID>
<GID>51280</GID>
<Kod>RNIKAKC1.6</Kod>
<Name>SB-800</Name>
<Ean>0018208048014</Ean>
<CommodityGroup>
<ID>86</ID>
<Name>AKCESORIA FOTO</Name>
<Path>
<Category>
<ID>60798</ID>
<Name>ARCHIWALNE</Name>
</Category>
</Path>
</CommodityGroup>
</Product>
....
Next products
...
My method code:
var MemoryStream = APIAccess.DownloadFileToStream("example.xml", "exampleContainer");
using (MemoryStream)
{
MemoryStream.Position = 0;
using (StreamReader StreamReader = new StreamReader(MemoryStream))
{
XmlSerializer serializer = new XmlSerializer(typeof(CommodityGroup));
var products = serializer.Deserialize(StreamReader);
}
}
Method DownloadFileToStream is working good, because it is useful in other classes.
I'm geting error:
InvalidOperationException: Products xmlns='' was not expected.
I want to create object of a Node CommodityGroup. I've created class selecting this node, coping it and pasting in the new class like Paste Special -> XML
Attributes of this class looks like:
[Serializable()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[XmlTypeAttribute(AnonymousType = true)]
[XmlRootAttribute(Namespace = "CommodityGroup", IsNullable = false)]
I don't know to fix it. When I'm adding into XML Serializer param new XmlRootAttribute("Products"), I'm getting "0" values.
Do you have any suggestions?
If you want to deserialize only part of an xml document, you should skip unnecessary nodes.
Do it using XmlReader.
using (StreamReader StreamReader = new StreamReader(MemoryStream))
using (var xmlReader = XmlReader.Create(StreamReader))
{
xmlReader.ReadToFollowing("CommodityGroup");
XmlSerializer serializer = new XmlSerializer(typeof(CommodityGroup));
var commodityGroup = (CommodityGroup)serializer.Deserialize(xmlReader);
}

How to reduce size of xml file programatically in c#

I have one xml file, it has 110KB
I've uploaded it here
In Notepad++ I'm using XML Tools plugin and pretty print (Ctrl+Alt+Shift+B) for code arrangement, like in the picture below
Also I have another plugin for Notepad++, "TextFX", I'm selecting all the text (Ctrl+A) and using Unwrap Text, like in the picture below
After these actions I'm saving my xml file and it has 100KB (uploaded it here ).
How can I do this action programatically in c# ?
Thanks in advance!
Are you asking about how to remove all space characters in the xml document? Please load it to the XmlDocument and read from OuterXml. You will get xml document in one line
var d = new Data();
var s = new XmlSerializer(d.GetType());
var sb = new StringBuilder();
var strStream = new StringWriter(sb);
s.Serialize(strStream, d);
Trace.WriteLine(sb.ToString());// formatted document
var xd = new XmlDocument();
xd.LoadXml(sb.ToString());
Trace.WriteLine(xd.OuterXml); // document without any surplus space character or linebreaks
Data is my custom class, please find it below. It does not contain any XML serialization control attributes. You can use any class instead of it.
public class Data
{
public string BIC;
public string Addressee;
public string AccountHolder;
public string Name;
public string CityHeading;
public string NationalCode;
public bool MainBIC;
public string TypeOfChange;
public DateTime validFrom;
public DateTime validTill;
public int ParticipationType;
public string Title { get; set; }
}
First trace produces well-formatted XML
<?xml version="1.0" encoding="utf-16"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MainBIC>false</MainBIC>
<validFrom>0001-01-01T00:00:00</validFrom>
<validTill>0001-01-01T00:00:00</validTill>
<ParticipationType>0</ParticipationType>
</Data>
and second trace output is single line:
<?xml version="1.0" encoding="utf-16"?><Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><MainBIC>false</MainBIC><validFrom>0001-01-01T00:00:00</validFrom><validTill>0001-01-01T00:00:00</validTill><ParticipationType>0</ParticipationType></Data>

Putting c# inside xml

I am using WinForms. I have an XML document that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<MarcusXMLFile xmlns:Responses="http://www.rewardstrike.com/XMLFile1.xml">
<response>
<greatmood>
<yes>
<replytocommand>
<answer>Yes.</answer>
<answer>Yes, sir.</answer>
<answer>Settings.Default.User</answer>
</replytocommand>
</yes>
</greatmood>
</response>
</MarcusXMLFile>
To read this xml document, I use:
private void Responses()
{
string query = String.Format("http://www.rewardstrike.com/XMLFile1.xml");
XmlDocument Responses = new XmlDocument();
Responses.Load(query);
XmlNode channel = Responses.SelectSingleNode("MarcusXMLFile");
if (QEvent == "yesreplytocommand")
{
XmlNodeList yesreplytocommand = Responses.SelectNodes("MarcusXMLFile/response/greatmood/yes/replytocommand/answer");
foreach (XmlNode ans in yesreplytocommand
.Cast<XmlNode>()
.OrderBy(elem => Guid.NewGuid()))
{
response = ans.InnerText;
}
}
}
and then to display:
QEvent = "yesreplytocommand";
Responses();
Console.WriteLine(response);
My problem is when it gets Settings.Default.User and displays it, I want it to display it as c# code so that it actually gets the value from the application. Right now it is actually displaying "Settings.Default.User". How do I do this?
First, you'll need a way to recognize which of your entries are literals and which are expressions. You could do it by adding an attribute to the XML node:
<?xml version="1.0" encoding="utf-8" ?>
<MarcusXMLFile xmlns:Responses="http://www.rewardstrike.com/XMLFile1.xml">
<response>
<greatmood>
<yes>
<replytocommand>
<answer>Yes.</answer>
<answer>Yes, sir.</answer>
<answer expression="true">DefaultSettings.User</answer>
</replytocommand>
</yes>
</greatmood>
</response>
</MarcusXMLFile>
Based on that you can modify your parsing code to either directly use the value from XML or evaluate it instead:
foreach (XmlNode ans in yesreplytocommand
.Cast<XmlNode>()
.OrderBy(elem => Guid.NewGuid()))
{
var attribute = ans.Attributes["expression"];
if (attribute != null && attribute.Value == "true")
{
Console.WriteLine(Evaluate(ans.InnerText));
}
else
{
Console.WriteLine(ans.InnerText);
}
}
There's still the problem of evaluating that expression. There's no easy built-in way to do that from C#. But you could use Dynamic Expresso. This is how Evaluate method could look like:
public string Evaluate(string expression)
{
var interpreter = new Interpreter();
interpreter.SetVariable("DefaultSettings", Settings.Default);
return interpreter.Eval<string>(expression);
}
As you can see, you'll still have to define the expression variables yourself. For the above to work, you will have to use DefaultSettings.User in your XML instead of Settings.Default.User. I already made that change in my sample XML at the beginning of the answer.
You should take a look at XML Serialization.
Really basic on how it works is that it can convert a struct or a class like this:
struct Foo
{
int bar = 0;
Vector2 obj = new Vector2(10, 50);
}
into this:
<?xml version="1.0" ?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"">
<bar>0</bar>
<obj>
<X>10</X>
<Y>50</Y>
</ojb>
</Foo>
And the other way around.
The methods used to load and save code looks like this:
public static void Save(string filepath, Foo foobject)
{
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
using (Stream stream = File.OpenWrite(filepath))
{
serializer.Serialize(stream, foobject);
}
}
public static Foo Load(string filepath)
{
Foo myFoo;
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
using (Stream stream = File.OpenRead(filepath))
{
myFoo = (Foo)serializer.Deserialize(stream);
}
}
It converts xml code to c# code, and other way around.
It cannot convert methods, but it can convert most properties and classes.

List XML Serialization Doubling Elements

I'm trying to serialize an object as XML and have been using a little tester to experiment with different object behaviors when serializing as XML. I know binary serializers are deep and that XML is shallow. However, it does seem that it tries to serialize a List composed within another object when using XML.
My issue is that I get copied data when I serialize a List. Code and output follow:
class Program
{
static void Main(string[] args)
{
TestSerializer original = new TestSerializer();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(original.GetType());
x.Serialize(Console.Out, original);
Console.WriteLine("\n\n\n");
using (MemoryStream stream = new MemoryStream())
{
x.Serialize(stream, original);
stream.Seek(0, SeekOrigin.Begin);
TestSerializer copy = x.Deserialize(stream) as TestSerializer;
x.Serialize(Console.Out, copy);
}
Console.ReadLine();
}
}
public class TestSerializer
{
public List<string> words = new List<string>();
public TestSerializer()
{
words.Add("word");
words.Add("anotherword");
}
}
And the corresponding output:
<?xml version="1.0" encoding="IBM437"?>
<TestSerializer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<words>
<string>word</string>
<string>anotherword</string>
</words>
</TestSerializer>
<?xml version="1.0" encoding="IBM437"?>
<TestSerializer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<words>
<string>word</string>
<string>anotherword</string>
<string>word</string>
<string>anotherword</string>
</words>
</TestSerializer>
As you can see, the list content is doubled up when "original" is serialized, then deserialized to "copy". Is there something I am missing as far this is concerned? It seems like there should not be duplicated data.
Put a breakpoint on the constructor of TestSerializer class. You will notice that it is called e.g. on the following line:
TestSerializer copy = x.Deserialize(stream) as TestSerializer;
So when you deserialize the object following happens
First instance of TestSerializer is created (populates the two values in the list) and it executes the default constructor
Dezerialization adds the items from the stream to the created object (now you have 4 items)

Categories