Why XmlSerializer.Order do this - c#

Can anybody explain why I am getting result below?
Class that should be instantiated:
[System.SerializableAttribute()]
public class SampleClass
{
[System.Xml.Serialization.XmlElementAttribute(Order = 10)]
public string Foo { get; set; }
[System.Xml.Serialization.XmlElementAttribute(Order = 5)]
public string Bar { get; set; }
}
XML used for de-serialization:
<?xml version="1.0" encoding="utf-8" ?>
<SampleClass>
<Foo>Test1</Foo>
<Bar>Test2</Bar>
</SampleClass>
I see Foo = Test1 (correct) and Bar = null (not correct) in de-serialized class.
This is all about Order attribute. It is on purpose set to wrong values. Everything works fine if values are 0 and 1.
Name and Order criteria do not match for both fields but for some reason one field is de-serialized while second one does not. I would rather expect to see Exception or both values = null or both fields resolved and de-serialized.
Is there any explanation for that?

You do see a serialization error actually.
It really comes down to design preference. The developers decided that they didn't want to except and abort the entire deserialization process, but rather notify and continue.
Using your XML.
<?xml version="1.0" encoding="utf-8" ?>
<SampleClass>
<Foo>Test1</Foo>
<Bar>Test2</Bar>
</SampleClass>
Consider this code to deserialize it.
XmlSerializer xs = new XmlSerializer(typeof(SampleClass));
XmlDeserializationEvents events = new XmlDeserializationEvents();
events.OnUnknownAttribute = (sender, e) => Debug.WriteLine("Unknown Attributed");
events.OnUnknownElement = (sender, e) => Debug.WriteLine("Unknwon Element");
events.OnUnknownNode = (sender, e) => Debug.WriteLine("Unknown Node");
events.OnUnreferencedObject = (sender, e) => Debug.WriteLine("Unreferenced Object");
SampleClass cs_de = (SampleClass)xs.Deserialize(XmlReader.Create(new StringReader(xml)), events);
Debug.WriteLine(cs_de.Foo);
Debug.WriteLine(cs_de.Bar);
When I use the correct ordering.
[System.Xml.Serialization.XmlElementAttribute(Order = 0)]
public string Foo { get; set; }
[System.Xml.Serialization.XmlElementAttribute(Order = 1)]
public string Bar { get; set; }
My output is
Foo
Bar
When I use the incorrect ordering.
[System.Xml.Serialization.XmlElementAttribute(Order = 10)]
public string Foo { get; set; }
[System.Xml.Serialization.XmlElementAttribute(Order = 5)]
public string Bar { get; set; }
My output is
Unknown Node
Unknwon Element
Foo
So why does Foo print out? Well my understanding is that Order is NOT the index. The Ordering only specifies that Foo needs to appear before Bar, not that Foo needs to be the 10th element and Bar needs to be the 5th. I think this makes the deserializer more flexible.

Related

newton json default string value not working?

When I seralise an object, for some string properties, I would like to output empty string other than ignore or output null.
According to newton's doc, I could do this:
public class Data
{
public int ProductId { get; set; }
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public string LargeData { get; set; }
}
However, in my test case, it still output null
Data D1 = new Data()
{
ProductId = 1
};
var b = JsonConvert.SerializeObject(D1);
The output is {"ProductId":1,"LargeData":null}. Am I doing something wrong?
Looking at DefaultValueHandling it doesn't look like there's any way of doing what you want.
The default value attribute is only used when deserializing, if the property isn't specified in the JSON. The ignore / include choices are the ones which are relevant when serializing, and those don't affect the value that's serialized - just whether or not it should be serialized.
Unless you've got code which actually sets the value to null, the simplest option would be to make the property default to "" from a .NET perspective:
public class Data
{
public int ProductId { get; set; }
public string LargeData { get; set; } = "";
}

C# XML Serialization removing wrapper element

I wrote because I have problem with XmlSerializer. I want XML in the following format:
<?xml version="1.0" encoding="utf-8"?>
<RootXML>
<e-Invoice>
<Version>1.03</Version>
</e-Invoice>
<TradeInvoice>
<Id>1</Id>
<Value>100</Value>
</TradeInvoice>
<e-Invoice>
<Version>1.03</Version>
</e-Invoice>
<TradeInvoice>
<Id>2</Id>
<Value>200</Value>
</TradeInvoice>
<e-Invoice>
<Version>1.03</Version>
</e-Invoice>
<TradeInvoice>
<Id>3</Id>
<Value>300</Value>
</TradeInvoice>
</RootXML>
So I created the following classes.
[XmlRoot("RootXML")]
public class Root
{
public Root()
{
RootBodies = new List<RootBody>();
}
[XmlElement("e-Invoice")]
public List<RootBody> RootBodies { get; set; }
}
public class RootBody
{
public RootBody()
{
TradeInvoice = new TradeInvoice();
EInvoiceInfo = new Version(); ;
}
[XmlElement("e-Invoice")]
public Version EInvoiceInfo { get; set; }
[XmlElement("TradeInvoice")]
public TradeInvoice TradeInvoice { get; set; }
}
public class Version
{
[XmlElement("Version")]
public string Version { get; set; }
}
public class TradeInvoice
{
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Value")]
public int Value { get; set; }
}
I have problem with removing wraper Elements (remove RootBody). I read similar topic like this link. But it does not solve my problem.
Before the actual explanation, let me point out a couple of very important things:
This XML is not very well designed and will cause a lot of other problems along the line (I'm guessing it is actually a lot more complicated than this).
The naming convention is inconsistent (e-Invoice and TradeInvoce)
The version number looks out of place, make sure this is really what they (whoever told you to do this) want before investing additional time.
This XML defines no namespaces (and probably doesn't have an XSD or DTD either)
Take a look at Google's XML design guidelines. You'll realize there is a lot of room for improvement.
There are a lot of different ways to do this, this is just one of them. I recommend you instead take it up with whoever is responsible for this design and try to change their mind.
Since you want to serialize e-Invoice and TradeInvoce without a wrapper element but still keep the order of elements (because they belong together) you need to make sure they have a common base class so they can be serialized from the same collection.
This abstract Invoice class simply tells the serializer which classes should be included during serialization via the XmlInclude attribute.
[XmlInclude(typeof(EInvoice))]
[XmlInclude(typeof(TradeInvoice))]
public abstract class Invoice
{
}
Your actual classes will be mostly unchanged
[XmlRoot("e-Invoice")]
public class EInvoice : Invoice
{
[XmlElement("Version")]
public string Version { get; set; }
}
[XmlRoot("TradeInvoice")]
public class TradeInvoice : Invoice
{
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Value")]
public int Value { get; set; }
}
Your data class doesn't need any XML-related Attributes anymore because as it is now, it can't be serialized into that format directly.
public class InvoicePair
{
public InvoicePair(EInvoice eInvoice, TradeInvoice tradeInvoice)
{
TradeInvoice = tradeInvoice;
EInvoiceInfo = eInvoice;
}
public EInvoice EInvoiceInfo { get; set; }
public TradeInvoice TradeInvoice { get; set; }
}
Your Root class has to be simplified a bit. Since we want EInvoce and TradeInvoice Elements on the same level but mixed together, we need to put them in the same collection. The XmlElement attributes will tell the serializer how to handle elements of different types without relying on the xsi:type attribute.
[XmlRoot("RootXML")]
public class Root
{
public Root()
{
RootBodies = new List<Invoice>();
}
[XmlElement(Type = typeof(EInvoice), ElementName = "e-Invoice")]
[XmlElement(Type = typeof(TradeInvoice), ElementName = "TradeInvoice")]
public List<Invoice> RootBodies { get; set; }
}
Serialization is quite straight-forward at this point. Simply add all elements to the collection of elements one after another:
public static void Serialize(IEnumerable<InvoicePair> invoices, Stream stream)
{
Root root = new Root();
foreach (var invoice in invoices)
{
root.RootBodies.Add(invoice.EInvoiceInfo);
root.RootBodies.Add(invoice.TradeInvoice);
}
XmlSerializer serializer = new XmlSerializer(typeof(Root));
serializer.Serialize(stream, root);
}
Deserialization isn't very hard either, but very prone to erros and makes a lot of assumptions:
public static IEnumerable<InvoicePair> Deserialize(Stream stream)
{
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = serializer.Deserialize(stream) as Root;
for (int i = 0; i < root.RootBodies.Count; i += 2)
{
yield return new InvoicePair(
(EInvoice) root.RootBodies[i],
(TradeInvoice) root.RootBodies[i+1]
);
}
}
Here is a working Demo Fiddle, that outputs the XML

Serializing list of nullable CDATA

I am trying to deserialize a XML file:
<?xml version="1.0" encoding="UTF-8"?>
<Foos xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FooList>
<Foo>
<Bar>bar value</Bar>
<Stack />
</Foo>
<Foo>
<Bar>bar value</Bar>
<Stack><![CDATA[This is some cdata, where <br> html code is left as is..]]></Stack>
</Foo>
</FooList>
</Foos>
Into the following class
[XmlRoot("Foos")]
public class Foos
{
[XmlArray("FooList")]
[XmlArrayItem("Foo")]
public List<Foo> FooList { get; set; }
}
public class Foo
{
public string Bar { get; set; }
[XmlElement("Stack", typeof(XmlCDataSection))]
public XmlCDataSection Stack {get; set; }
}
The problem is, that the resulting FooList of Foos only contains one (1) element, which is the first Foo block, with the Slack property set to null. If i add a CDATA value to stack, like with the second Foo block, then i will end up with a list of both elements.
For some reason, the deserialization stops, after reaching a CDATA value which is null.
I have tried creating a private string and creating the setter and getter of Stack, such that is uses the private property to store the CDATA string. This doesn't change anything.
Any suggestions?
Thank you.
Get to a cdata section through a string. Object model:
[Serializable]
[XmlRoot("Foos", Namespace="", IsNullable=false)]
public partial class Foos {
[XmlArrayItem ("Foo", IsNullable=false)] public Foo[] FooList { get; set; } }
[Serializable] public partial class Foo {
public string Bar { get; set; }
[XmlIgnore] public string Stack { get; set; }
[XmlElement("Stack", IsNullable=true)]
public XmlCDataSection StackCDATA {
get { return new XmlDocument().CreateCDataSection(Stack ?? String.Empty); }
set {
Stack = value == null
? String.Empty
: ( String.IsNullOrWhiteSpace(value.Value)
? String.Empty
: value.Value); } }
public override string ToString() { return String.Format("Bar:{0} Stack:{1}", Bar, Stack); } }
The right way to specify a null element is <Stack xsi:nil='true' />. With this addition, your deserialization will be flawless. However, on the first occurrence of <Stack />, the deserializer will encounter an unknown node. Workaround in case you are missing this attribute, subscribe to UnknownElement:
var fooSrlz = new XmlSerializer(typeof(Foo), new XmlRootAttribute { ElementName = "Foo", IsNullable = true} );
var foosMissedByDeserializer = new List<CData.Foo>();
srlz.UnknownElement += (/*object*/ sender, /*XmlElementEventArgs */ e) => {
using (TextReader tr = new StringReader(e.Element.OuterXml)) {
var foo = (CData.Foo)fooSrlz.Deserialize(tr);
foosMissedByDeserializer.Add (foo);
} };
Merge the deserialization result with the foosMissedByDeserializer above.

Is there any way to deserialize xml to object with specified keyword?

As you can see in blow code I want to deserialize custom xml to object, and I don't know how many code attribute there, so I want to filter all attribute that contains the "code", and map it with List property. Is there any way to implement this feature?
<Root>
<FirstElement FirstCode="1" SecondCode="2" ThirdCode="3" Id="1" Name="Element" />
</Root>
public class MappedClass
{
[XmlAttribute(AttributeName = "Name")]
public string Name {get;set;}
[XmlAttribute(AttributeName = "Id")]
public int Id {get;set;}
[?]
public List<Code> Codes {get;set;}
}
public Class Code
{
public string Name {get;set;}
public string Value {get;set;}
}
I guess you can. Use UnknownAttribute event to place unknown attribute to Codes collection:
var serializer = new XmlSerializer(typeof(MappedClass));
serializer.UnknownAttribute +=
(s, e) =>
{
(e.ObjectBeingDeserialized as MappedClass).Codes.Add(new Code { Name = e.Attr.Name, Value = e.Attr.Value });
};

C# serialize simple object to xml. no namespace no Nulls

I got the following code:
public class Alarm:IDisposable
{
MemoryStream _tmp;
readonly XmlDocument _doc;
private readonly XmlSerializerNamespaces _ns;
private readonly XmlSerializer _x = new XmlSerializer(typeof(Alarm));
public int? ID { get; set; }
public string SourceSystem { get; set; }
public string SensorName { get; set; }
public string ModelName { get; set; }
public int? Severity { get; set; }
public int? Duration { get; set; }
public bool? Status { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public Alarm()
{
_tmp = new MemoryStream();
_doc = new XmlDocument();
_ns = new XmlSerializerNamespaces();
_ns.Add("", "");
}
public string OuterXml()
{
//Add an empty namespace and empty value
_x.Serialize(_tmp, this, _ns);
_tmp.Position = 0;
_doc.Load(_tmp);
return _doc.OuterXml;
}
public void Dispose()
{
if (_tmp!=null)
{
_tmp.Close();
_tmp = null;
}
}
}
I get as output"
<?xml version=\"1.0\"?>
<Alarm><ID>1</ID><SourceSystem>HMer</SourceSystem>
<SensorName>4</SensorName><Severity d2p1:nil=\"true\" xmlns:d2p1=\"http://www.w3.org/2001/XMLSchema-instance\" />
<Duration>500</Duration><Status>true</Status>
<StartTime>2011-07-19T12:29:51.171875+03:00</StartTime>
<EndTime d2p1:nil=\"true\"
xmlns:d2p1=\"http://www.w3.org/2001/XMLSchema-instance\" /></Alarm>
I wanna get:
<Alarm><ID>1</ID><SourceSystem>HMer</SourceSystem><SensorName>4</SensorName>
<Duration>500</Duration><Status>true</Status>
<StartTime>2011-07-19T12:29:51.171875+03:00</StartTime></Alarm>
meaning no xmlns stuff, no tag where value is null.
please assist meh
Add the following properties to your Nullable fields:
[XmlIgnore]
public bool EndTimeSpecified { get { return EndTime.HasValue; } }
This lets XmlSerializer (which is a bit dumb about Nulllable fields) realize that no EndTime has been specified, and, thus, the field does not need to be serialized.
Here's the documentation of this feature:
Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".
Here's a related SO question that offers more details and explanations:
How to make a value type nullable with .NET XmlSerializer?
About removing the XML processing instruction (i.e., the <?xml ... ?> part), there's also a SO question about that:
Omitting XML processing instruction when serializing an object

Categories