I have a XML file looking like this:
<Stamdata xmlns="http://schemas.datacontract.org/2004/07/a">
<Liste xmlns:d3p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<d3p1:KeyValuePairOfStringDatagxNgnmsk>
<d3p1:key>key value</d3p1:key>
<d3p1:value>
......
</d3p1:value>
</d3p1:KeyValuePairOfStringDatagxNgnmsk>
</Liste>
</Stamdata>
Any my models looks like
[DataContract(Name = "Stamdata", Namespace = "http://schemas.datacontract.org/2004/07/a")]
public class Stamdata
{
[DataMember]
public KeyValuePair<string, Data>[] Liste { get; set; }
}
[DataContract(Name = "Data", Namespace = "http://schemas.datacontract.org/2004/07/System.Collections.Generic")]
public class Data
{
//.... Many properties
}
My problem is that the list keeps containing 0 elements, even thought the xml contains around 100.
And I'm not sure id the "gxNgnmsk" at the end of <d3p1:KeyValuePairOfStringDatagxNgnmsk> means anything... the one behind the response doesn't know wither what it is.
The "gxNgnmsk" at the end of <d3p1:KeyValuePairOfStringDatagxNgnmsk> is the cause of your problem. The presence of this suffix is explained in Data Contract Names for Generic Types:
Special rules exist for determining data contract names for generic types...
By default, the data contract name for a generic type is the name of the type, followed by the string "Of", followed by the data contract names of the generic parameters, followed by a hash computed using the data contract namespaces of the generic parameters. ... When all of the generic parameters are primitive types, the hash is omitted.
The "gxNgnmsk" is the hash. And if I use your classes to generate XML from instances created in memory, I get a different hash:
<Stamdata xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/a">
<Liste xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<d2p1:KeyValuePairOfstringDatatwCi8m_S7>
<d2p1:key>key value</d2p1:key>
<d2p1:value />
</d2p1:KeyValuePairOfstringDatatwCi8m_S7>
</Liste>
</Stamdata>
Apparently your Data type does not have the correct data contract namespace, causing an inconsistent hash to get generated. (And, from experiment, neither "http://schemas.datacontract.org/2004/07/System.Collections.Generic" nor "http://schemas.datacontract.org/2004/07/a" nor "" seem to generate the correct hash. Possibly I could guess the correct namespace from a complete XML sample, but there's not enough information in the simplified XML provided.)
So, what can be done to fix this?
You could get the "one behind the response" to tell you the correct data contract name and namespace for all contract types including Data. This is necessary anyway for data contract equivalence when sending data over the wire so they should be able to provide this information.
If the "one behind the response" provides a WSDL (and they should) you should be able to auto-generate a client which should just work.
But in the absence of the above, you can work around the problem by creating a collection with a custom collection data contract with the hash baked in:
[DataContract(Name = "Stamdata", Namespace = "http://schemas.datacontract.org/2004/07/a")]
public class Stamdata
{
[DataMember]
public DataList Liste { get; set; }
}
[CollectionDataContract(
Namespace = "http://schemas.datacontract.org/2004/07/System.Collections.Generic",
ItemName = "KeyValuePairOfStringDatagxNgnmsk")]
public class DataList : List<KeyValuePair<string, Data>>
{
public DataList() : base() { }
public DataList(IEnumerable<KeyValuePair<string, Data>> list) : base(list) { }
}
With this version of Stamdata the sample XML can be deserialized without knowing the correct namespace for Data.
Related
Let's say I have this little json snippet:
{
"Type": "Bar",
"BarOnly": "This is a string readable when deserialized to the Bar class only, as declared in my type key"
}
I also have these three classes:
public class Base
{
public enum SampleEnum
{
Bar,
Baz,
}
public SampleEnum Type
{
get;
set;
}
}
public class Bar : Base
{
public string BarOnly
{
get;
set;
}
}
public class Baz : Base
{
public string BazOnly
{
get;
set;
}
}
Based on the Type property in the json snippet, I'd like to have it deserialize to either Bar or Baz.
My first idea was to first deserialize it to the Base class, and then use its type and a switch statement to deserialize the JSON again to its respective class. (Using Newtonsoft.Json)
var type = JsonConvert.DeserializeObject<Base>(json).Type;
string message = "";
switch (type)
{
case (Base.SampleEnum.Bar):
message = JsonConvert.DeserializeObject<Bar>(json).BarOnly;
break;
case (Base.SampleEnum.Baz):
message = JsonConvert.DeserializeObject<Baz>(json).BazOnly;
break;
}
Console.WriteLine(message);
Needless to say that this process is extremely redundant, tedious and, since the switch statement is hard-coded, not very "dynamic" at all.
Another idea was to use a generic class as the base class instead and passing in the type of the actual class it should deserialize to, but then I end up with the same switch statement to figure out what that class should be.
Since you can't map enums to class types, I also thought about using a Dictionary to map the possible enum values to their class counterparts; this still makes the mapping process hard-coded though.
Is there any way I can dynamically get the corresponding class to deserialize to based on the type property of the json object?
EDIT: There seems to be some confusion about how this is supposed to be used and how the data is fetched; let me provide some background information.
I'm iterating through a directory with a lot of different spreadsheet files, mostly CSVs and XML files. Each of these feeds have a "meta file", describing how to process their content. This includes checksums, delimiters and other information. They also declare of what type their parent file is (CSV, XML etc). Hence, they share a lot of common properties (like the Base class in my example), but also have their own set of properties. They derive from an abstract class that requires them to implement a function that returns an instance of the corresponding feed processing class, initialized with values directly from within the meta class. I hope this makes sense.
#OguzOzgul commenting is correct. I've done this countless of times for objects that are composed with interfaces that need to be serialized and deserialized.
See TypeNameHandling for Newtonsoft:
https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm
Your json file will look ever so slightly different:
{
"$type": "SomeNamespace.Bar",
"BarOnly": "This is a string readable when deserialized to the Bar class only, as declared in my type key"
}
If you use
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
}
During serialization, it will add the full type name of all objects to make sure newtonsoft knows what their type is during deserialization (given you use the same settings then). That way you do not have to write your own custom code for type detection.
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 :/
I need to deserialize some JavaScript object represented in JSON to an appropriate C# class. Given the nice features of automatic properties, I would prefer having them in these classes as opposed to just having fields. Unfortunately, the .NET serialization engine (at least, by default) totally ignores automatic properties on deserialization and only cares about the backing field, which is obviously not present in the JavaScript object.
Given that there's no standard way to name backing fields and to be honest I don't even want to bother with the "let's create a JavaScript object that looks like it had C# backing fields" approach as it sounds a bit dirty, the only way I could serialize JavaScript fields to C# auto-properties if I could force the serialization engine to somehow ignore the backing field and use the property directly. Unfortunately, I can't figure out how this is done or if this can be done at all. Any ideas would be appreciated.
EDIT: Here's an example:
Javascript:
function Cat()
{
this.Name = "Whiskers";
this.Breed = "Tabby";
}
var cat = new Cat();
This is then serialized to "{Name: 'Whiskers'}".
The C# class:
[Serializable()]
public class Cat
{
public string Name { get; set; }
public string Breed { get; set; }
}
And the deserialization code, that fails:
new DataContractJsonSerializer(typeof(Cat)).ReadObject(inputStream);
And it is apparent from the exception that it fails because it is looking for the backing field.
EDIT2: Here's the exception, if that helps (no inner exceptions):
System.Runtime.Serialization.SerializationException
"The data contract type 'Test.Cat'
cannot be deserialized because the
required data members
'<Name>k__BackingField, <Breed>k__BackingField' were not
found."
What's happening here is the deserializer is trying to guess the name of your backing fields.
You can solve this by adding explicit mappings (DataContract/DataMember attributes) like this:
[DataContract]
public class Cat
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Breed { get; set; }
}
You can do this with JavaScriptSerializer found in the System.Web.Script.Serialization namespace:
JavaScriptSerializer serializer = new JavaScriptSerializer();
Cat c = serializer.Deserialize<Cat>(jsonString);
I have POCO objects with automatic properties and this works just fine.
EDIT: I wrote about JSON Serializers in .NET which compares this serializer with DataContractJsonSerializer.
baretta's answer solved the k__BackingField bloat for me. Just a tiny addendum that you can decorate this class to auto serialize into either XML or JSON in a similar way:
[Serializable, XmlRoot, DataContract]
public class Cat
{
[XmlElement]
[DataMember]
public string Name { get; set; }
[XmlElement]
[DataMember]
public string Breed { get; set; }
}
... and then use a DataContractJsonSerializer or XmlSerializer to prepare it for your endpoint.
I'm assuming you are passing data via a web service. If you are using the WebService class with the ScriptMethod attribute uncommented-out, the web service methods can read JSON natively. They even use the same JavaScriptSerializer that was mentioned above. If you are using WCF I'm a little more fuzzy on the logic.
But make sure your JSON object are returning data for EVERY property in your class. In your error, there is mention of a Breed property that is not in your example.
Also, on the JavaScript side, do to the dynamic nature of JavaScript it is easy to add new properties to your objects. This can sometimes lead to circular references. You should remove any extra data that you might have added (just as you are sending data via the web method, then add it again once you are done).
I have a fairly simple DAL assembly that consists of an SalesEnquiry Class which contains a List<T> of another Vehicle class.
We'll be receiving XML files by email that I'm wanting to use to populate instances of my SalesEnquiry class, so I'm trying to use de-serialization.
I've added XMLRoot/XMLElement/XMLIgnore attributes to both classes as I think is appropriate. However, when I try de-serializing, the parent SalesEnquiry object is populated but no child Vehicle objects.
I understand that de-serializing List<T> can be tricky, but I'm not sure why, how to avoid problems, or even if this is why I'm struggling.
While debugging, I've successfully serialized a Vehicle object on it's own, so I'm assuming that I'm heading in the right direction, but when I de-serialize the SalesEnquiry XML (which contains one or more child Vehicles), the List<Vehicle> isn't populated.
Where am I going wrong?
Update:
In a test project, I serialized an SalesEnquiry containing two vehicles and save to a file. I then loaded the file up an de-serialized it back into a different SalesEnquiry object. It worked!
So what was the difference? The vehicles were recorded as follows:
<?xml version="1.0" encoding="utf-8"?>
<enquiry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<enquiry_no>100001</enquiry_no>
<vehicles>
<Vehicle>
<vehicle_type>Car</vehicle_type>
<vehicle_make>Ford</vehicle_make>
<vehicle_model>C-Max</vehicle_model>
...
The thing to note is that Vehicle has a an initial capital, whereas my incoming XML doesn't. In my Vehicle class, I gave the class an [XmlRoot("vehicle")] attribute, which I though would make the link, but clearly it doesn't. This makes sense, I guess, because although Vehicle is a class in it's own right, it's merely an array item within a List inside my SalesEnquiry.
In which case, the question is - How do I annotate the Vehicle class such that I can map the incoming XML elements (<vehicle>) to my list items (Vehicle)? [XmlArrayItem] (or [XmlElement] for that matter) are 'not valid on this declaration type'.
In this example, I can request that the people who generate the XML use <Vehicle> rather than <vehicle>, but there may be situations where I don't have this freedom, so I'd rather learn a solution than apply a workaround.
Conclusion:
By adding [XmlArrayItem("vehicle", typeof(Vehicle))] to the existing decoration for my List, the XML is now able to de-serialize fully. Phew!
Here's a working pair of classes with appropriate decorations:
(Note: The XmlAnyElement and XmlAnyAttribute are optional. It's a habit I'm in to promote the flexibility of the entity.)
[XmlType("enquiry")]
[XmlRoot("enquiry")]
public class Enquiry
{
private List<Vehicle> vehicles = new List<Vehicle>();
[XmlElement("enquiry_no")]
public int EnquiryNumber { get; set; }
[XmlArray("vehicles")]
[XmlArrayItem("Vehicle", typeof(Vehicle))]
public List<Vehicle> Vehicles
{
get { return this.vehicles; }
set { this.vehicles = value ?? new List<Vehicle>(); }
}
[XmlAnyElement]
public XmlElement[] AnyElements;
[XmlAnyAttribute]
public XmlAttribute[] AnyAttributes;
}
public class Vehicle
{
[XmlElement("vehicle_type")]
public string VehicleType { get; set; }
[XmlElement("vehicle_make")]
public string VehicleMake { get; set; }
[XmlElement("vehicle_model")]
public string VehicleModel { get; set; }
}
I am reading data from another system that returns XML that provides the following information for each field:
"Field Name", "Data Type", and "Size". The "Data Type" returned is either: Alpha, LAlpha, RAlpha, CAlpha, or Numeric. Sometimes there are other attributes returned as well, such as Casing. I would like to create objects to model the data returned and also have to generate xml with element values formatted according to the given data type to send back to the other system to perform transactions. I started off creating an object that would represent each property on the object like:
//Type T represents the data type of the Value property
public class OtherSystemField<T> : IOtherSystemField{
public string OtherSystemFieldName { get; set;}
public OtherSystemDataTypeEnum OtherSystemDataType { get; set;}
public int Size { get; set;}
public T Value { get; set;}
public string ToOtherSystemString() { ....};
}
//Class using the data field
public OtherSystemEntityClass {
public OtherSystemField<string> f1 { get; set; }
public OtherSystemEntityClass () {
f1 = new OtherSystemField<string>() {
OtherSystemFieldName = "x", Size = 4, OtherSystemDataType = ...};
f1.Value = "DefaultStringValue";
}
}
The question is does this representation of the other system's data fields make the most sense, using this type of object to model a field from the other system instead of a .Net data type property that is associated with some meta-data?. Any opinions on, say, having the property have attributes containing these values like:
public OtherSystemEntityClass {
[OtherSystemFieldName("SomeFieldName", 5, OtherSystemDataTypeEnum.RAlpha)]
public string f1 { get; set; }
}
Here is an example of the XML:
<COLUMNS>
<COLUMN header="FieldX" dspname="FieldX" dbname="FIELDX" type="NUMERIC" size="4" />
<COLUMN header="FieldY" dspname="FieldY" dbname="FIELDY" type="ALPHARIGHT" size="14"/>
</COLUMNS> <COLS> <COL><![CDATA[ 1000]]></COL> <COL><![CDATA[ 102]]></COL> </COLS>
I look forward to everyone's feedback - I have no doubts I will hear good perspectives.
Performance is not of a huge concern, as the number of fields is relatively small. Also, this is implemented in .Net 3.5
I'm not sure what the "Alpha" types are, but the newly-added XML snippet is clearly describing a table schema. If you can pull off a mapping, I might actually use a DataTable instead of creating any custom classes at all.
I wouldn't be too surprised to find out that the XML is literally serialized directly from something similar to a .NET DataSet/DataTable but in a different language/platform. Those CDATA tags are not consumer-friendly. But if you can map Alpha values to some enum type (most likely a custom one you create), then you can just add a column of that type to a DataTable.
Do you already know the Field Name, Data Type, Size, etc of your fields or is this something that you need to determine at run time? If you already know all the information about the fields at design time, then I like the attribute approach. I've done the very same when dealing with incoming Xml from old systems. You can then deserialize the Xml to your objects, and you're good to go.
If you do not know the format of the Xml at design time, that is, you need to set the attribute values at run time, then using attributes will be more difficult than your first approach. You would need to set the attributes with reflection, which would be more work than just setting a property in the first solution.