Incorrect XML deserialization - c#

I have the following class:
public class FtpDefinition
{
public FtpDefinition()
{
Id = Guid.NewGuid();
FtpServerAddress = string.Empty;
FtpPortSpecified = false;
FtpPort = "21";
}
[System.Xml.Serialization.XmlElement("Id")]
public System.Guid Id { get; set; }
[System.Xml.Serialization.XmlElement("FtpServerAddress")]
public string FtpServerAddress { get; set; }
[System.Xml.Serialization.XmlElement("FtpPortSpecified")]
public bool FtpPortSpecified { get; set; }
[System.Xml.Serialization.XmlElement("FtpPort")]
public string FtpPort { get; set; }
}
I have a method that gets the following XML string, and using the .net XML deserialization capability
deserializes it into an object of type FtpDefinition.
<FTPDefinition>
<Id>a0a940a7-6785-41be-ac3a-75ba5d4c13ee</Id>
<FtpServerAddress>ftp.noname.com</FtpServerAddress>
<FtpPortSpecified>false</FtpPortSpecified>
<FtpPort>21</FtpPort>
</FTPDefinition>
The problem is, that although the Id and FtpServerAddress fields get populated properly, FtpPort gets
populated with an empty string, and what's more weird is that FtpPortSpecified gets populated with the bool value TRUE instead of FALSE.
I replaced the automatic properties in the above code with actual return\... = value old style getter\setter, so that I can catch the setter getting hit. I was suspecting there's some user code setting the value, but this is not the case. In the call stack it clearly shows that the .net deserialization code is calling the setter with the value TRUE, but one can also see that the XML string provided as parameter to the deserializing method has the correct value (FALSE).
The deserialization code is simple:
XmlSerializer xs = ...(objectType);
using (StringReader stringReader = new StringReader(xml))
{
return xs.Deserialize(stringReader);
}
Please help me figure out what's going on.

The Specified suffix has some special behavior in XML Serialization. Simply change FtpPortSpecified to something else.
http://msdn.microsoft.com/en-us/library/office/bb402199(v=exchg.140).aspx

Related

Why does the JSON2CSharp online converter decorate each property with a [JsonProperty("...")] attribute?

I have this JSON response from an API:
{
"arguments": {
"Configuration": {
"Building_Configuration.Parameters_SP.fixtureStrategy_SP": "ETA",
"Building_Configuration.Parameters_SP.dimensionSelection_SP": "Imperial",
"Building_Configuration.Parameters_SP.controllerRobotic_SP": false,
"Building_Configuration.Parameters_SP.controllerBACNet_SP": false
}
}
}
I have this Root.cs Model file that I used the JSON to C# Converter to make that corresponds to the JSON above in my solution in C# Visual Studio 2019:
public class Root
{
public Arguments arguments { get; set; }
}
public class Arguments
{
public Configuration Configuration { get; set; }
}
public class Configuration
{
[JsonProperty("Building_Configuration.Parameters_SP.fixtureStrategy_SP")]
public string BuildingConfigurationParametersSPFixtureStrategySP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.dimensionSelection_SP")]
public string BuildingConfigurationParametersSPDimensionSelectionSP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.controllerRobotic_SP")]
public bool BuildingConfigurationParametersSPControllerRoboticSP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.controllerBACNet_SP")]
public bool BuildingConfigurationParametersSPControllerBACNetSP { get; set; }
}
I'm trying to access and return the value of BuildingConfigurationParametersSPFixtureStrategySP (the first property in the Configuration class above) in this manner:
public string CreateExecPost()
{
/*...everthing here works fine down through the end of this method. I can set
breakpoints and step through the following code and look at the values of all the
following variables and all is well
....*/
var payload = new StringContent(newPost, Encoding.UTF8, "application/json");
var result = client.PostAsync(endpoint, payload).Result.Content.ReadAsStringAsync().Result;
Root MyObject = JsonConvert.DeserializeObject<Root>(result);
return MyObject.arguments.configuration.BuildingConfigurationParametersSPFixtureStrategySP;
} //The correct value for BuildingConfigurationParametersSPFixtureStrategySP is returned and all is well!
So, the question is why does the converter generate an attribute like...
[JsonProperty("Building_Configuration.Parameters_SP.fixtureStrategy_SP")]
...above each of the { get; set; } statements? I've researched and read about JsonProperty and JsonPropertyAttribute, but I'm still not fully clear on it. I'm not seeing what the tool uses to signal it to generate the attribute or why it does it.
This tool generates code with the Json.net library by default, and the official documentation doesn’t explain much on this class: https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_JsonProperty.htm
There are other similar questions on how to use this, for example:
What [JsonProperty] used for in c#?
The general usage of this class is when you want to, or need to, rename a property. And the last one is relevant here.
The json parser normally tries to match the property names 1:1 with your class properties.
But whenever the json property name contains reserved keywords, language syntax, or otherwise illegal property names, you need to rename it.
In your example the name Building_Configuration.Parameters_SP.fixtureStrategy_SP contains periods. If you would try to name your get;set property like this, the code would not compile.
The site that generates the code knows this, and will add the required JsonProperty to map the full json property name to the class field that was renamed to BuildingConfigurationParametersSPFixtureStrategySP (the illegal characters were removed)
See for valid property names: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names
And for reference: Accessing properties with a dot in their name

Xml Empty Tag Deserialization

Could you please help me to find the solution to deserialize xml file which contains an empty tag?
Example is here:
<Report>
<ItemsCount></ItemsCount>
</Report>
And I want to deserialize it into object of class like:
public class Report{
public int? ItemsCount { get;set;}
}
my xml schema which i'm using in deserialization is:
[XmlRoot]
public partial class Report
{
private int? itemsCount;
[XmlElement(IsNullable = true)]
public int? ItemsCount {
get
{
return itemsCount;
}
set
{
itemsCount = value;
}
}
It works well if the ItemsCount tag is missing at all, but if it is exist and is empty at the same moment, in that case it throwing the exception regarding lines there this tag is located in xml.
I saw a lot of links here while trying to find the solution, but without success.
And also, i don't want to just ignore the tag for all the cases, i want to get a null value instead then it is empty.
XmlSerializer is trying to convert string.Empty value of tag to integer and failing. Change your property as below to convert data type to string:
[XmlElement]
public string ItemsCount {
get
{
return itemsCount;
}
set
{
itemsCount = value;
}
This will set property Itemscount to empty in the above case.
For null value for the above property the xml should be as below:
<ItemsCount xs:Nil='true'/>
How about this approach?
Define the class as follows:
public class Report
{
[XmlIgnore]
public int? ItemsCount { get; set; }
}
Due to the XmlIgnore attribute, this tag will be treated as unknown.
When creating the serializer add the event handler:
var xs = new XmlSerializer(typeof(Report));
xs.UnknownElement += Xs_UnknownElement;
In the event handler interpret an empty string as null:
private void Xs_UnknownElement(object sender, XmlElementEventArgs e)
{
var report = (Report)e.ObjectBeingDeserialized;
if (e.Element.InnerText == string.Empty)
report.ItemsCount = null;
else
report.ItemsCount = int.Parse(e.Element.InnerText);
}
Use the serializer as usual:
Report report;
using (var fs = new FileStream("test.xml", FileMode.Open))
{
report = (Report)xs.Deserialize(fs);
}
To my understanding, the described behaviour is correct; if the tag ItemsCount is missing, its value is null; if it is empty, its value cannot be converted from "" to a value of int?. That being said, it would be possible to implement some custom parsing into the accessors of ItemsCount, which would have to be of type string. However, this seems more like a workaround to me. If possible, the document should be changed to begin with.

XmlSerializer.Deserialize for a sub class (not an array)

So this has had me stumped for hours... I have an xml structure that looks like this:
<custom>
<priceLower>999999</priceLower>
<priceUpper>1000001</priceUpper>
<investment>true</investment>
<offtheplan>false</offtheplan>
<office>
<name>Melbourne Office</name>
<officeName>Head Office</officeName>
... more elements removed
</office>
</custom>
In my application I have a Custom class, which deserializes from the above xml fine, defined as follows:
[Serializable]
public class Custom : BaseEntity, IDataModel
{
[XmlElement("investment")]
public string Investment { get; set; }
[XmlElement("offtheplan")]
public string Offtheplan { get; set; }
[XmlElement("priceLower")]
public Decimal? PriceLower { get; set; }
[XmlElement("priceUpper")]
public Decimal? PriceUpper { get; set; }
[XmlElement("office")]
public Office Office { get; set; }
And my Office Object defined as follows:
[Serializable]
public class Office : BaseEntity, IDataModel
{
// temporary for debugging purposes:
private string _officeName;
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("officeName")]
public string OfficeName { get; set; }
[XmlElement("addressL1")]
public string Address1 { get; set; }
... more fields removed }
The Deserialize code (called by a helper class, and receives a Property Object containing A Custom object, which contains an Office object) looks like this:
XmlSerializer s = null;
XmlAttributeOverrides attrOverrides = null;
/// if it's a Residential type, do it this way
if (typeof(T) == typeof(Residential))
{
attrOverrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes();
var attr = new XmlElementAttribute();
attr.ElementName = "office";
attr.Type = typeof(Office);
attrs.XmlElements.Add(attr);
attrOverrides.Add(typeof(Office), "office", attrs);
s = new XmlSerializer(typeof(T), attrOverrides);
}
s = attrOverrides == null
? new XmlSerializer(typeof(T))
: new XmlSerializer(typeof(T), attrOverrides);
var obj = s.Deserialize(stream);
return (T)obj;
SO... The Custom object deserializes perfectly.. no issues there. But the office one doesn't - all it's properties always come through as null.
Is there a way to specify exactly which element in the xml tree contains the data for the Office object? i've tried moving the Office Object to sit at the same level as Custom (on the Property object) which makes more sense really, but that didn't work either - i moved it to be under Custom to match the xml structure as I can't change that, and i couldn't find a way to specify where it should get it's data from.
Another bit of weirdness I've experienced here... I have added a Serializer function, which basically creates a new XML file from the deserialized objects. I can debug all the way through to where the Serialize function is called - and if I peek inside the object getting serialized before it happens, i can see that the Office object contains only null values. But the serializer actually serializes data into my new XML file.
Here is where it gets even weirder. If i peek into the object before Serialize() is called, then it will always serialize an empty element. BUT, if i don't peek inside that object before the serialization happens, it'll serialize data into there. I have verified this a bunch of times - without fail that is the behaviour. Has anybody ever seen anything like this? Is the framework playing tricks on me, and if so whY???
UPDATE:
Just to clarify, my XML looks something like this (I only showed an exert above):
<propertyList>
<residential>
<custom>
<property1>
<office>
<officeName>
Office Name Here
</officeName>
</office>
</custom>
</residential>
</propertyList>
So there's a few levels of nesting there, which i possibly the issue, though I'm thinking it's more of a VS issue.
The deserializer is working on the full XML, and deserialising down to a class structure like this:
Residential : Property : BasePropertyType
Contains Custom Object
Contains Office Object
Both Custom and Office objects are instantiated on the Residential object. I have tried putting the Office object on the Custom object instead (to match the xml structure) but that made no difference. Custom serializes correctly, Office does not.
Is it at all possible that the Visual Studio debugger is the red herring here. As i mentioned before, if i debug and take a look at the deserialized object, it shows it as empty, and when i then serialize it back to XML it comes through as empty. But if i don't debug and just step over without taking a look at the object, all the properties serialize correctly to the XML. It's making it kind of difficult to move on and do the rest of the data processing i need to when i can't debug what's going on there. Has anybody seen behaviour in VS like this before??
As to your first question, XmlSerializer can handle simple hierarchies so in your example XmlAttributeOverrides are not necessary:
[Serializable]
[XmlRoot("custom")]
public class Custom
{
[XmlElement("investment")]
public string Investment { get; set; }
[XmlElement("offtheplan")]
public string Offtheplan { get; set; }
[XmlElement("priceLower")]
public Decimal? PriceLower { get; set; }
[XmlElement("priceUpper")]
public Decimal? PriceUpper { get; set; }
[XmlElement("office")]
public Office Office { get; set; }
}
[Serializable]
public class Office
{
// temporary for debugging purposes:
private string _officeName;
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("officeName")]
public string OfficeName { get; set; }
[XmlElement("addressL1")]
public string Address1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
string xml = #"<custom>
<priceLower>999999</priceLower>
<priceUpper>1000001</priceUpper>
<investment>true</investment>
<offtheplan>false</offtheplan>
<office>
<name>Melbourne Office</name>
<officeName>Head Office</officeName>
</office>
</custom>";
XmlSerializer s = new XmlSerializer(typeof(Custom));
// Works fine without this
//XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
//var attrs = new XmlAttributes();
//var attr = new XmlElementAttribute();
//attr.ElementName = "office";
//attr.Type = typeof(Office);
//attrs.XmlElements.Add(attr);
//attrOverrides.Add(typeof(Office), "office", attrs);
//s = new XmlSerializer(typeof(Custom), attrOverrides);
using (StringReader reader = new StringReader(xml))
{
Custom c = (Custom)s.Deserialize(reader);
}
}
}
Ahahahaha... I'm such a goof! Had a workmate run through this with me, and we found something really silly that i'd done, namely:
public string ToString()
{
Name = null;
OfficeName = null;
Address1 = null;
Address2 = null;
City = null;
State = null;
Postcode = null;
Phone = null;
Banner = null;
Logo = null;
StringBuilder sb = new StringBuilder();
sb.Append(String.Format("Name:{0} / OfficeName: {1} / Address1: {2} / Address2: {3} / City: {4} / State: {5} / Postcode: {6} / Phone: {7} / Banner: {8} / Logo: {9}",
Name, OfficeName, Address1, Address2, City, State, Postcode, Phone, Banner, Logo));
return sb.ToString();
}
So every time i took a look at the object in the debugger, it was calling my ToString() override, which was then nuking all the values.
Don't i feel sheepish. LOL

XML Deserialization in C#

I have one class which was serialized before. We have the xml output from it.
When we open the project we deserialize the xml to get the preserved objects.
Now i have added new bool property to class and since it is a new property, old xmls don't have this attribute. My deserialization works fine but assigns false to bool property, I want it to set true if its not there in XML. How can i achieve this ? I tried like this
public bool? _flag;
[XmlElement("Flag")]
public bool? flag
{
get
{
if (null != _flag)
{
return _flag;
}
return true;
}
set { _flag= value; }
}
You just need to add your default constructor and set it there. Here is an example:
public MyObject()
{
Flag = true;
}
EDIT
I'm not sure what's going on in your code, but this works perfectly fine:
public class MyObject
{
public MyObject()
{
Flag = true;
}
public bool Flag { get; set; }
public string Name { get; set; }
}
First, I didn't have the bool property there and serialized it to a file.. then for step 2, I added that bool property and the constructor.. then deserialized it from disk and it showed true, which is what I expected.
Please review your code, as I expect something else is going on. If you need help, post the full class here.

XML deserialization - throwing custom errors

So I have the following method:
private int? myIntField
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public int? IntField{
get {
return this.myIntField;
}
set {
this.myIntField= value;
}
}
Now, I am deserializing xml from a post, if for whatever reason I am getting a string, such as "here is the int field: 55444" instead of 55444, the error I get in response is: Input string was not in a correct format. which isn't very specific, especially considering I will have more than one int field I need to verify.
Originally, I was planning something like this:
private string myIntField
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public int? IntField{
get {
return this.myIntField.CheckValue();
}
set {
this.myIntField= value;
}
}
Where CheckValue performs a try-parse to an Int32, and if it fails it returns a null and adds an error to a list. However, I can't seem to nail this set-up for the generated classes.
Is there I way I can throw a specific error if I am getting strings in place of ints, DateTimes, etc?
It's easy if you have schema(s) for you XML and validate it against schema before deserializing. Suppose you have schema(s) for your XML, you can initialize a XmlSchemaSet, add your schema(s) in it and the:
var document = new XmlDocument();
document.LoadXml(xml); // this a string holding the XML
document.Schemas.XmlResolver = null; //if you don't need to resolve every references
document.Schemas.Add(SchemaSet); // System.Xml.Schema.XmlSchemaSet instance filled with schemas
document.Validate((sender, args) => { ... }); //args are of type ValidationEventArgs and hold problem if there is one...
Personally I think this is a better approach, because you can validate your XML before deserializing and be sure the XML is correct otherwise the deserializer will most probably throw an exception if something is wrong and you will almost never be able to show a meaningful feedback to the user...
P.S. I recommend creating schema(s) describing the XML
The "Input string was not in a correct format" messages comes from a standard System.FormatException raised by a call to int.Parse, added to the automatically generated assembly that does the deserialization. I don't think you can add some custom logic to that.
One solution is to do something like this:
[XmlElement("IntField")]
[Browsable(false)] // not displayed in grids
[EditorBrowsable(EditorBrowsableState.Never)] // not displayed by intellisense
public string IntFieldString
{
get
{
return DoSomeConvert(IntField);
}
set
{
IntField = DoSomeOtherConvert(value);
}
}
[XmlIgnore]
public int? IntField { get; set; }
It's not perfect, because you can still get access to the public IntFieldString, but at least, the "real" IntField property is used only programmatically, but not by the XmlSerializer (XmlIgnore), while the field that's holding the value back & forth is hidden from programmers (EditorBrowsable), grids (Browsable), etc... but not from the XmlSerializer.
I have three approaches for you.
Assuming your data is being entered by a user in a user interface, use input validation to ensure the data is valid. It seems odd to allow random strings to be entered when it should be an integer.
Use exactly the approach you suggest above. Here's an example using LINQ Pad
void Main()
{
using(var stream = new StringReader(
"<Items><Item><IntValue>1</IntValue></Item></Items>"))
{
var serializer = new XmlSerializer(typeof(Container));
var items = (Container)serializer.Deserialize(stream);
items.Dump();
}
}
[XmlRoot("Items")]
public class Container
{
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
public class Item
{
[XmlElement("IntValue")]
public string _IntValue{get;set;}
[XmlIgnore]
public int IntValue
{
get
{
// TODO: check and throw appropriate exception
return Int32.Parse(_IntValue);
}
}
}
Take control of serialization using IXmlSerializable, here's another example
void Main()
{
using(var stream = new StringReader(
"<Items><Item><IntValue>1</IntValue></Item></Items>"))
{
var serializer = new XmlSerializer(typeof(Container));
var items = (Container)serializer.Deserialize(stream);
items.Dump();
}
}
[XmlRoot("Items")]
public class Container
{
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
public class Item : IXmlSerializable
{
public int IntValue{get;set;}
public void WriteXml (XmlWriter writer)
{
writer.WriteElementString("IntValue", IntValue.ToString());
}
public void ReadXml (XmlReader reader)
{
var v = reader.ReadElementString();
// TODO: check and throw appropriate exception
IntValue = int.Parse(v);
}
public XmlSchema GetSchema()
{
return(null);
}
}

Categories