Read XML with C# with custom deserializer - c#

I'm trying to deserialize a piece of XML offered by some API. However this API is dumb, for example, a bool is not true, but True. And having an element
[XmlElement("Foo")]
public bool Foo { get;set; }
and then the matching XML:
<...><Foo>True</Foo></...>
does NOT work, because True is not a valid representation of bool (the API is written in Python, which is, I think, more forgiving).
Is there any way to put some attribute on my property Foo to say to the system: when you encounter this element, put it through this converter class?
Edit:
The XML is large, and most of them are stupid, not directly convertible objects, like 234KB, which I need to parse to the exact value.
Do I need to write a wrapper property for each of them?

You could use a backing property:
public class MyModel
{
[XmlIgnore]
public bool Foo
{
get
{
return string.Equals(FooXml, "true", StringComparison.OrdinalIgnoreCase);
}
set
{
FooXml = value.ToString();
}
}
[XmlElement("Foo")]
public string FooXml { get; set; }
}

Related

Binding a ProtoInclude to the corresponding RuntimeTypeModel binding doesn't work

I'm currently trying out protobuf-net, and have got problem with serialization of the following class structure:
[ProtoContract]
public abstract class WorkerResponseBase
{
[ProtoMember(1)]
public WorkerErrorMessage ErrorMessage { get; set; }
[ProtoMember(2)]
public bool IsSuccess { get; set; }
[ProtoMember(3)]
public bool IsError { get; set; }
protected WorkerResponseBase()
{
}
protected WorkerResponseBase(bool isSuccess, [CanBeNull] string errorMessage)
{
IsSuccess = isSuccess;
IsError = !isSuccess;
ErrorMessage = new WorkerErrorMessage(errorMessage);
}
}
[ProtoContract()]
public class WorkerResponse<TPayload> : WorkerResponseBase
{
[ProtoMember(4)]
public TPayload PayloadOrNull { get; private set; }
....
}
When I first tried it with a concrete class as in e.g. WorkerResponse<LoginResult>, I was only able to have PayloadOrNull serialized.
Alright, I got to google a bit on how to make this work. I found this answer: ProtoBuf-Net ProtoInclude Generic Type Subclass
People mentioned, that [ProtoInclude] is required.
So, for testing this, I was trying to decorate the WorkerResponseBase with [ProtoInclude(100, typeof(WorkerResponse<LoginResult>))]. Bingo, serialization works perfectly.
Now, as you might be able to imagine, this is a very generic container for a response, so I wouldn't want to have to define all possible TPayloads in the base class, so a bit further down in the linked comment I found, that I should also be able to do this dynamically, and eventually via reflection.
So for testing purposes, I did the following instead of the [ProtoInclude]:
RuntimeTypeModel.Default.Add(typeof(WorkerResponseBase), false)
.AddSubType(100, typeof(WorkerResponse<LoginResult>));
However, when I run this, no serialization works at all, not even the serialization of the PayloadOrNull.
So, what am I doing wrong here?
The false in your code (to Add) is basically saying "and I'm going to control everything myself, don't process the properties and their attributes - I'll tell you explicitly". Since you're not doing that, you probably want to pass true. In the pending V3 API, this is an optional parameter that defaults to true.

Produce different serialized JSON for a given class in different scenarios

Update 1: for reasons I won't go into, I want to avoid having anything other than the properties to be persisted in my entity objects. This means no extra properties or methods...
I have an entity called Entity1 with (say) 10 public properties. In
one place in my code I want to output serialized JSON with (say) 3 of
those fields, in a second place I need to output 7 fields and in a
third place I might need to output (say) all 10 fields. How do I do
this using Newtonsoft's JSON library?
I can't use [JsonIgnore] or [DataMember] as that will apply to all
cases, so I won't be able to create "custom views" of the data (my own
terminology :-).
I tried to achieve this using an interface:
public interface Entity1View1
{
string Property1;
string Property2;
string Property5;
}
had Entity1 implement Entity1View1 and I passed an
IList<Entity1View1> to the JSON serializer (the objects were
actually just Entity1 objects). Didn't work: the serializer output
all the 10 public properties of Entity1.
The only other way I could think of was to implement
Entity1Wrapper1, Entity1Wrapper2 etc. type of classes where each
object would hold a corresponding instance of Entity1 and in turn
expose only those public properties that correspond to the properties
I want to show in "View1", "View2" etc. Then I pass lists of these
wrapper objects to the serializer (should work, haven't tried it yet).
Is there a better way?
If it matters, here's my configuration:
.Net 4.5
MVC 5
Don't know it that's the best way... but that's one.
One good point is that it will work either with json serialization or xml serialization, for example (which you may don't mind at all).
You can use ShouldSerialize<yourpropertyName> to manage what is serialized or not. <yourpropertyName> must match exactly the name of the property you wanna manage.
For example
public class Entity {
//assuming you want the default behavior to be "serialize all properties"
public Entity() {
ShouldSerializeProperty1 = true;
ShouldSerializeProperty2 = true;
ShouldSerializeProperty3 = true;
}
public string Property1 {get;set;}
public bool ShouldSerializeProperty1 {get;set;}
public string Property2 {get;set;}
public bool ShouldSerializeProperty2 {get;set;}
public int Property3 {get;set;}
public bool ShouldSerializeProperty3 {get;set;}
}
Then you could do, before all your serialization (of course, this could / should be extension methods).
var list = myListOfEntity;
//serialization1
foreach (var element in list) {
element.ShouldSerializeProperty3 = false;
}
//or serialization2
foreach (var element in list) {
element.ShouldSerializeProperty2 = false;
element.ShouldSerializeProperty3 = false;
}
I just wanted to make sure that this was the final step in processing.
You can create anonymous objects to serialize based on circumstance:
var json1Source1 = new {
Property1 = entityView1.Property1,
Property3 = entityView1.Property3
};
var json1Source2 = new {
Property2 = entityView1.Property2,
Property3 = entityView1.Property3
};
You can create jsonSource1 (or 2, 3, 4 etc) as anonymous objects that capture just what you need and then serialize them. The serializer will not care that they are anonymous.
Update 1:
To conditionally serialize a property, add a method that returns boolean with the same name as the property and then prefix the method name with ShouldSerialize..
This means that the solution suggested by Raphaël Althaus doesn't work as it relies on properties, whereas the serializer's documentation mentions that it has to be a method. I have verified that only a method returning a bool works as expected.
Original:
I finally went with a mix of Wrapper classes and the methodology suggested by Raphaël Althaus (with modifications): use Wrappers where some amount of sophistication may be required and use Raphaël's suggestion when simplicity will do.
Here's how I am using wrappers (intentionally left out null checks):
public class Entity1View1
{
protected Entity1 wrapped;
public Entity1View1(Entity1 entity)
{
wrapped = entity;
}
public String Property1
{
get { return wrapped.Property1; }
}
public String Property2
{
get { return wrapped.Property2; }
}
public String Property3
{
get { return wrapped.Property3.ToUpper(); }
}
}
This allows me to modify properties as their values are returned (as done with Property3 above) and lets me leverage inheritance to create new ways of serialization. For example, I can flatten the structure/hierarchy:
public class Entity1View2 : Entity1View1
{
pulic Entity1View2(Entity1 entity) : base(entity) { }
public long? SubEntityID
{
get { return wrapped.SubEntity.ID; }
}
}
For simpler cases where complexity/transformation of this sort is not required, I can simply use the ShouldSerialize* methods.
Same entity classes, different serialization outputs.

When serializing/deserializing XML data, do the XML elements and the class attributes have the same name?

For example:
Here's a simple class.
class Hero
{
public string faction;
public string name;
public HeroType herotype;
}
And here's the XML counterpart.
<Hero>
<android>
<Faction>evil</Faction>
<nombre>android</nombre>
</android>
</Hero>
Do the attributes have to be exactly the same in order to serialize information?
My main purpose is to "load" information to the Hero class with information from my XML file.
You can decorate your fields with [XmlElement(...)] to specify an alternate name. Also they do not have to be in the same order. However the Xml you specified doesn't fit the structure of the classes you specified.
Modify your Xml to something like this:
<Hero>
<Name>android</Name>
<Faction>evil</Faction>
<HeroType>Agility</HeroType>
</Hero>
Unless you specify otherwise (as Aviad P. says, by decorating a property with the [XmlElement()] attribute), the name in the XML will be matched exactly to the property name, and vice versa.
The ordering should be insignificant. I say "should be" because whether not it actually is insignificant depends on how you've designed your class.
While in general it's good practice to have property setters be side-effect-free, when you're dealing with XML deserialization, it's essential. During deserialization, properties will be set to their values in the order that they appear in the XML.
So if you have this class:
public class Test
{
private string _Foo;
public string Foo { set { _Foo = value; _Baz="Foo"; } get { return _Foo; }}
private string _Bar;
public string Bar { set { _Bar = value; _Baz="Bar"; } get { return _Bar; }}
private string _Baz;
public string Baz { set { _Baz = value; } get { return _Baz; }}
}
then XML in which Foo appears before Bar will set Baz to "Foo", while XML in which Bar appears before Foo will set Baz to "Bar".

How can I get XmlSerializer to encode bools as yes/no?

I'm sending xml to another program, which expects boolean flags as "yes" or "no", rather than "true" or "false".
I have a class defined like:
[XmlRoot()]
public class Foo {
public bool Bar { get; set; }
}
When I serialize it, my output looks like this:
<Foo><Bar>true</Bar></Foo>
But I would like it to be this:
<Foo><Bar>yes</Bar></Foo>
Can I do this at the time of serialization? I would prefer not to have to resort to this:
[XmlRoot()]
public class Foo {
[XmlIgnore()]
public bool Bar { get; set; }
[XmlElement("Bar")]
public string BarXml { get { return (Bar) ? "yes" : "no"; } }
}
Note that I also want to be able to deserialize this data back again.
Ok, I've been looking into this some more. Here's what I've come up with:
// use this instead of a bool, and it will serialize to "yes" or "no"
// minimal example, not very robust
public struct YesNo : IXmlSerializable {
// we're just wrapping a bool
private bool Value;
// allow implicit casts to/from bool
public static implicit operator bool(YesNo yn) {
return yn.Value;
}
public static implicit operator YesNo(bool b) {
return new YesNo() {Value = b};
}
// implement IXmlSerializable
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader) {
Value = (reader.ReadElementContentAsString() == "yes");
}
public void WriteXml(XmlWriter writer) {
writer.WriteString((Value) ? "yes" : "no");
}
}
Then I change my Foo class to this:
[XmlRoot()]
public class Foo {
public YesNo Bar { get; set; }
}
Note that because YesNo is implicitly castable to bool (and vice versa), you can still do this:
Foo foo = new Foo() { Bar = true; };
if ( foo.Bar ) {
// ... etc
In other words, you can treat it like a bool.
And w00t! It serializes to this:
<Foo><Bar>yes</Bar></Foo>
It also deserializes correctly.
There is probably some way to get my XmlSerializer to automatically cast any bools it encounters to YesNos as it goes - but I haven't found it yet. Anyone?
Very simple. Use a surrogate property. Apply XmlIgnore on the actual property. The surrogate is a string, and must use the XmlElement attribute that takes a element-name override. Specify the name of the actual property in the override. The surrogate property serializes differently based on the value of the actual property. You must also provide a setter for the Surrogate, and the setter should set the actual property appropriately, for whatever value it serialized. In other words it needs to go both ways.
Snip:
public class SomeType
{
[XmlElement]
public int IntValue;
[XmlIgnore]
public bool Value;
[XmlElement("Value")]
public string Value_Surrogate {
get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; }
set { Value= (value=="Yes, definitely!"); }
}
}
click here for full compilable source example.
Making a bool value serialize as "yes" or "no" changes the data type from being a boolean at all. Instead, can you add a separate property which evaluates a boolean and returns "yes" or "no" as appropriate for it's data type? Maybe you could even force "yes" or "no" by making the return type be an enum which only specifies those values.
public YesOrNo DoYouLoveIt
{
get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; }
}
That might be overkill, but might answer your need. The only reason I bring up an enum for such a simple value is you'd be restricting the values vs. allowing any string.
I use the property method, but instead of checking to see if the string is equal to yes or no, I prefer to check if the string starts with (case insensitive) "YT1". This allows the file to contain true, True, t, T, y, Y, yes, Yes, 1, etc. all which will evaluate to true. While I can specify that false is false, False, f, F, n, N, no, No, 0, etc., anything that doesn't match the true still evaluates to false.
Your property example is probably the simplest way you could do it. If it helps, I believe you don't need to make it a public property, since the attribute implements ISerializable on the class behind your back. To enable deserialization, you should be able to just implement set { Bar = value == "yes"; }
#Blorgbeard:
If you have more then one of these YesNo classes in an object class,
make sure to read the entire element.
public void ReadXml(XmlReader reader)
{
string element = reader.ReadOuterXml();
int startIndex = element.IndexOf('>') + 1;
int length = element.LastIndexOf('<') - startIndex;
string text = (element.Substring(startIndex, length).ToLowerInvariant();
Value = (text == "yes");
}
Otherwise this might cause problems.
The ReadXml method must reconstitute your object using the information that was written by the WriteXml method.
When this method is called, the reader is positioned at the start of the element that wraps the information for your type. That is, just
before the start tag that indicates the beginning of a serialized
object. When this method returns, it must have read the entire element
from beginning to end, including all of its contents. Unlike the
WriteXml method, the framework does not handle the wrapper element
automatically. Your implementation must do so. Failing to observe
these positioning rules may cause code to generate unexpected runtime
exceptions or corrupt data.
What you're needing to do sounds more like a display issue. If your application allows, you will be better off keeping the data type as a boolean and displaying Yes/No in your user interface.

Why isn't my public property serialized by the XmlSerializer?

This is one i struggled with for ages so thought I'd document somewhere. (Apologies for asking and answering a question.)
(C# .net 2.0)
I had a class that was being serialized by XmlSerializer, I added a new public property however it wasn't being included in the output XML.
It's not mentioned in the docs anywhere I could find, but public properties must have a set as well as a get to be serialized! I guess this is because it assumes that if you're going to serialize then you'll want to deserialize from the same file, so only serializes properties that have both a set and a get.
As mentioned, most properties must have both a getter and setter; the main exception to this is lists - for example:
private readonly List<Foo> bar = new List<Foo>();
public List<Foo> Bar {get { return bar; } } // works fine
which will work fine; however, if XmlSerializer finds a setter - it demands that it is public; the following will not work:
public List<Foo> Bar {get; private set;} // FAIL
Other reasons it might not serialize:
it isn't public with get and set (or is readonly for a field)
it has a [DefaultValue] attribute, and is with that value
it has a public bool ShouldSerializeFoo() method that returned false
it has a public bool FooSpecified {get;set;} property or field that returned false
it is marked [XmlIgnore]
it is marked [Obsolete]
Any of these will cause it not to serialize
The point about getter+setter is made in the 3rd paragraph on the "Intro to Xml Serialization" page. It's actually in a call-out box. Can't miss it!
Intro-to-XML Serialization http://www.freeimagehosting.net/uploads/2f04fea2db.png
(having a little too much fun with Freeimagehosting.net)
Also properties that return null are not serialized!
if you don't want to implement proper Setters (because maybe you are neither wanting to deserialize or change an objects value) you can just use dummy setters like this set { }, so that the XMLSerializer works, but nothing happens if you use the Setter...
i.E.
public string ID { get { return _item.ID.ToString(); } set { } }
And if your class inherits a list and also has its own members, only the elements of the list get serialized. The data present in your class members is not captured.
Took some time figuring out this!
One more thing to add about serialization of collections:
The XmlSerializer ignores collections of interfaces!
And by that I mean ignore. While you will get an exception for a line like:
public IFoo Foo { get; set; }
you will not get an exception for:
public ICollection<IFoo> LotsOfFoos { get { return this.fooBackingField; } }
You can implement the IXmlSerializer and do the serialization manually, and benefit from serializing properties, and vice versa, deserializing them using constructors / private field assignment.

Categories