Deserialize XML element presence to bool in C# - c#

I'm trying to deserialize some XML from a web service into C# POCOs. I've got this working for most of the properties I need, however, I need to set a bool property based on whether an element is present or not, but can't seem to see how to do this?
An example XML snippet:
<someThing test="true">
<someThingElse>1</someThingElse>
<target/>
</someThing>
An example C# class:
[Serializable, XmlRoot("someThing")]
public class Something
{
[XmlAttribute("test")]
public bool Test { get; set; }
[XmlElement("someThingElse")]
public int Else { get; set; }
/// <summary>
/// <c>true</c> if target element is present,
/// otherwise, <c>false</c>.
/// </summary>
[XmlElement("target")]
public bool Target { get; set; }
}
This is a very simplified example of the actual XML and object hierarchy I'm processing, but demonstrates what I'm trying to achieve.
All the other questions I've read related to deserializing null/empty elements seem to involve using Nullable<T>, which doesn't do what I need.
Does anyone have any ideas?

One way to do it would be to use a different property to get the value of the element, then use the Target property to get whether that element exists. Like so.
[XmlElement("target", IsNullable = true)]
public string TempProperty { get; set; }
[XmlIgnore]
public bool Target
{
get
{
return this.TempProperty != null;
}
}
As even if an empty element exists, the TempProperty will not be null, so Target will return true if <target /> exists

Can you explain why you dont want to use nullable types?
When u define an int (as opposed to int?) property in ur poco, it doesnt really represent the underlying xml, and u will simply get the default values for those variables.
IF u assume you wont get empty/null strings or integers with the value 0 in ur xml, youcan used the method Balthy suggested for each of ur properties, or use the method described here
Generally i think its a better idea to create a schema to describe ur xml, and generate classes based on it, while using nullable types, if you really want your classes represent the underlying data.

Related

Get property default value as expression using reflection

In order to build a custom transpiler, I'm trying to get the default value of all the properties inside a class as an expression, and not as a value itself.
Let me bring you some examples to clarify what I'm trying to do and what I've done/tried/investigated so far.
Source code could be the following one:
const string DATETIME_NOW = "____DATETIME_NOW____";
public class Person {
[DefaultValue("Foo")]
public string Name { get; set; } = "Foo";
[DefaultValue(DateTime.Now)] // This is not doable: "An attribute argument must be a constant expression"
public DateTime DateOfBirth { get; set; } = DateTime.Now;
[DefaultValue(DATETIME_NOW)]
public string DateOfBirthStringed { get; set; } = DATETIME_NOW; // which acts like DateTime.Now.ToString()
}
The ultimate goal of the transpiler, is to obtain a Javascript class that looks like this:
class Person {
name: string = "Foo";
dateOfBirth: Date = new Date(Date.now());
dateOfBirthStringed : Date = Date.now();
}
My current, and working, implementation is the use of DefaultValue attribute with some constants strings used when the default value is an expression (e.g. DateOfBirthStringed).
What I'm doing is using reflection on Person, getting all the PropertyInfo, looking for their DefaultValue attribute, and then checking if the given default value are some fixed constants like DATETIME_NOW.
This works, but I've a couple of problems:
The type in attribute DefaultValue could be different from the type of the property.. No type check :(
If I only have the DefaultValue, when I write new Person(), the default values are not actually set from the attribute.
Therefore, I need to write the default value after { get; set; }, but:
Or I wrote both attribute and default value, but then I should manually mantain synchronized them.
I write only the default value, but then I've no way to get it with reflection.
About point 3.2, why I can't get the default value via reflection?
Suppose the Person class defined above; if I use reflection, I need to instantiate it, but once instantiated, Person.DateOfBirth has an actual DateTime value, and I cannot know it was coming from DateTime.Now.
Also, if Person would be abstract.. Well, no way to instantiate it.
So, basically, the only way I could perfectly transpile the code is to read the code as a tree, something like parseTreeCode(typeof(Person)). At that point, I should navigate the tree and be able to do everything I want.
I did find Roslyn, which allows me to parse C# code, but.. It parses "stringed" code, and not code that belongs to the same project. I thought "well, get the file where Person is defined, and parse the whole file", but unfortunately, once the program is running, I cannot get the file where Person is defined.. I mean, I can do typeof(Person).Assembly, and getting the Assembly.. But it would be an assembly, so it would not be good.
At this point, I'm thinking that there is no real solution for this, but maybe I'm missing some C# packages/features that could help me

How to change xml attribute while deserialization with xmlserializer c#?

Is there any way of modifying attribute value when deserializing xml using XmlSerializer?
For instance, I have such xml:
<chunkList>
<chunk id="ch1" type="p">
<sentence id="s1">
<tok>
<orth>XXX</orth>
<lex disamb="1">
<base>XXX</base>
<ctag>subst:sg:nom:f</ctag>
</lex>
</tok>
</sentence>
</chunk>
</chunkList>
I want to deserialize chunk element into Chunk class and set attribute id="ch1" to Id property - is there any way of trimming this ch substring and asigning number 1 to property of type int?
[XmlAttribute("id")] //maybe there is some attribute to achive this?
public int Id { get; set; }
I have read some of MSDN documentation but didn't found any solution.
There is no elegant way to achieve this using a single attribute. The only way I know to achieve the desired result is to make use of [XmlIgnore] and to create a second property specifically for the stringified xml ID, and a localized converter property for your internal integer value. Some along the lines of:
[XmlAttribute("id")]
public string _id_xml {get; set;}
[XmlIgnore]
public int Id {
// convert local copy of xml attribute value to/from int.
get => int.Parse(_id_xml.Replace("ch",""));
set => _id_xml = $"ch{value}";
}
My converter here is very basic and clearly you will need to improve it and consider error handling.
The serializer will operate against the [XmlAttribute] as normal, but pass over the [XmlIgnore]. Your c# code could use either.
Unfortunately, the XmlSerializer requires public properties, so you can not hide the _id_xml property from your code, but you could use [Obsolete] to signal a warning in the compiler.
You could do the conversion to/from int with the _id_xml getter & setter, but doing this could be problematic when managing errors during serialization.

Ignoring JSON enum value that doesn't exist in target model class during deserialization [duplicate]

This question already has answers here:
How can I ignore unknown enum values during json deserialization?
(6 answers)
Closed 5 years ago.
So I'm working on a API wrapper with C#, I deserialize JSON data to a response model class using Newtonsoft.Json library. In the response model class, I have a list of sub-items, which each of contain a list of sub-items. These are defined like this:
public List<StatModel> Stats { get; set; }
each StatModel has a property which basically equals the name:
public Stat Stat { get; set; }
these are automatically deserialized, because each Stat is defined in an enum like this:
[EnumMember(Value = "Avg Walk Distance")]
AverageWalkDistance,
Now the problem is, if something changes in the actual API, the wrapper doesn't work since it doesn't have definition for the specified Stat. So this means if they add a new Stat to the API, the wrapper won't work until I manually add definition for it, like in the above code block.
So the question is, how can I ignore values that don't have corresponding Stat property available, or can I somehow re-design the whole thing so this doesn't happen? I'm guessing I have to define all new values by myself either way.
Here's the project on GitHub to get a better understanding of what I actually mean: https://github.com/eklypss/PUBGSharp
The Requester does the deserializing and returns a StatResponse, which has a list of sub-items called StatsRoot which each have their own list of StatModels which are the actual stat objects causing this issue. Each type of Stat is defined in the Enum/Stat.cs file.
If you don't want to create your own tolerant version of StringEnumConverter, you could use Json.NET's exception handling features:
public class StatModel
{
const string StatName = "label";
[JsonProperty(StatName)]
[JsonConverter(typeof(StringEnumConverter))]
public Stat Stat { get; set; }
public string Value { get; set; }
public int? Rank { get; set; }
public double? Percentile { get; set; }
[OnError]
void OnError(StreamingContext context, ErrorContext errorContext)
{
if (errorContext.OriginalObject == this && StatName.Equals(errorContext.Member))
{
errorContext.Handled = true;
}
}
}
When an exception is thrown while deserializing StatModel or any of its nested objects, its OnError() method will be called to possibly handle the error. The method checks to see whether the exception was thrown
While deserializing this specific object, AND
While deserializing the "label" member.
If so, the exception is swallowed. You could also take the opportunity to set a flag in the model indicating that the Stat property was invalid.
You can have json.net ignore missing members.
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
var json = JsonConvert.DeserializeObject<MyClass>(jsonStr, settings);
Refer to the question below, this guy had same issues as you have.
How can I ignore unknown enum values during json deserialization?
However,
Even if you somehow manage to ignore new Stats, it's not good for your wrapper as consumers of your wrapper need to wait for you to add that new Enum.
So,
If I were you, I would think to change Stat from Enum to string, so whenever a new Stat comes, neither you need to change anything nor consumers of your wrapper, would not have to wait.

How can I deserialize xml with 2 different types of the same xml element and property name?

In the database, we have an xml field that contains 2 validation schemas; the old one does not have a namespace, the new one does. The reason for this is that we had to version one of the properties. Here is an example of the differences:
Version 1
<PropertyA>
<PropertyA1>false</PropertyA1>
<PropertyA2>3.23</PropertyA2>
</PropertyA>
Version 2
<ts:PropertyA xmlns:ts="http://www.example.com/v2">
<ts:PropertyA1>false</ts:PropertyA2>
<ts:PropertyA2>
<ts:PropertyA2a>
<ts:PropertyA2a1>City 1</ts:PropertyA2a1>
<ts:PropertyA2a2>3.23</ts:PropertyA2a2>
</ts:PropertyA2a>
<ts:PropertyA2b>
<ts:PropertyA2b1>City 2</ts:PropertyA2b1>
<ts:PropertyA2b2>1.21</ts:PropertyA2b2>
</ts:PropertyA2b>
</ts:PropertyA2>
</ts:PropertyA>
Basically, we just create multiple options for PropertyA2...
So now the isue is deserialization. This object needs to be deserialized into the same data object in the app code and the problem is that the element name is the same so the serializer is obviously having trouble figuring out which object to deserialize into since sometimes the database will return Version 1 and sometimes it will return Version 2.
Here is an example of the data class being used for serialization and my current approach that isn't quite working:
[Serializable]
public class MyDataClass
{
// ... other stuff
[XmlElement(Name = "PropertyA", typeof(V1.PropertyA), Namespace = "")]
public V1.PropertyA PropertyAV1 { get ;set; }
[XmlElement(Name = "PropertyA", typeof(V2.PropertyA), Namespace = "http://www.example.com/v2")]
public V2.PropertyA PropertyAV2 { get; set; }
}
[Serializable]
public class V1.PropertyA
{
public bool PropertyA1 { get; set; }
public decimal PropertyA2 { get; set; }
}
[Serializable]
public class V2.PropertyA
{
public bool PropertyA1 { get; set; }
public List<SomeOtherThing> PropertyA2 { get; set; }
}
When I go to deserialize V1, it works fine. When I go to deserialize V2, i get an error Did not expect <ts:PropertyA xmlns:ts="http://www.example.com/v2"> so I'm thinking there's a parameter I'm missing in the deserialize method:
public MyDataClass Deserialize(string xml)
{
var s = new XmlSerializer(typeof (MyDataClass));
MyDataClass info = null;
using (var r = new StringReader(xml))
{
info = (MyDataClass) s.Deserialize(r);
}
return info;
}
I believe you can set the expected namespace in the serializer, but since I don't know what the namespace is going to be until I actually inspect the xml document, I'm not sure how to proceed.
So my question is this: Is what I'm trying to do even possible? Am I on the right track? Is there a better solution that is maybe less contrived? How can I have the serializer deal with the new namespace and deserialize to the correct properties?
You can't.
The problem here is that you have to hardcode MyDataClass according to a single XMLSchema. If the XMLSchema alters, MyDataClass is no longer a valid target for the XMLSerializer's deserialize method, which is why you're getting the 'Did not expect ...' error message. In this case, when reading the V2 xml data stream, the deserialize method tries to fill MyDataClass#PropertyAV1 with the content of <ts:PropertyA2> and there is no way of telling it to instead fill MyDataClass#PropertyAV2. Even if there was a way to achieve this, you'd be stuck with an undefined value for MyDataClass#PropertyAV1 in the object of type MyDataClass.
So there are two solutions to the problem at hand :
a) Stick with XMLSerializer and define class MyDataClass like so
public class MyDataClass
{
// only one Property here, as there's only one root element in the xml
// and this single Property is not bound to a specific XML element
[XmlAnyElement]
public PropertyA PropertyA { get ;set; }
}
You then have to analyze the contents of PropertyA yourself and build some logic around it, see here for more details :
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlanyelementattribute.aspx
b) Dispense the XMLSerializer, read the XML data stream with XMLReader and do the all the parsing of the xml yourself, also add logic to create the according C# objects, depending on the the type of xml you've read.
Obviously, both solutions require more coding on the C# side, but with solution b) you'll have the chance of gaining a performance benefit, as XMLSerializer#deserialize most probably builds a DOM tree to create the C# object from, which the XMLReader doesn't do.
It seems that what I was trying to do was either unachievable or no one with the right level of xml fu saw this thread :(.
So anyway, what I ended up doing was adding an extra column to the database with the version number of the xml contract. Since everything in there was the same, I just called it V1.
I then read that info out into app code and used the version number to drive a factory. Basically, if v1, then deserialize to this, if v2, deserialize to this other thing.
And of course, to support that, I simply created a new data object that had the appropriate structure to support v2. I'm not happy with it, but it works and is flexible enough :/

Read XML with C# with custom deserializer

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; }
}

Categories