51

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."

Community
  • 1
  • 1
Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176
  • Can you post an example of what you mean? – John Saunders Jun 03 '09 at 16:09
  • @John Saunders - let's say I got a JavaScript Object called "Cat" with a single field called "Name" with the value "Whiskers". Let's say this is represented in JSON. I want to grab this JSON and serialize it to an instance of C# Cat class (written by me) so that the serialization process populates the automatic property "Name" of the instance with "Whiskers". – Tamas Czinege Jun 03 '09 at 16:11
  • ...but the problem is that the serializer is looking for the backing field (e.g. "k_backingField" but not necessarily with this name") and not "Name". – Tamas Czinege Jun 03 '09 at 16:14
  • What serializer are you using? – Szymon Rozga Jun 03 '09 at 16:14
  • siz: System.Runtime.Serialization.Json namespace. The JSonSerializer2 serializer can do the trick but I would prefer using tools in the framework if it's possible. – Tamas Czinege Jun 03 '09 at 16:15
  • Please post the exception, complete with any InnerException, by displaying ex.ToString(). – John Saunders Jun 03 '09 at 16:23
  • 2
    Also, I haven't used this, but maybe you should use [DataContract] and [DataMember] instead of [Serializable]? – John Saunders Jun 03 '09 at 16:24

4 Answers4

83

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; }
}
baretta
  • 7,385
  • 1
  • 25
  • 25
21

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.

aleemb
  • 31,265
  • 19
  • 98
  • 114
5

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.

Community
  • 1
  • 1
amelvin
  • 8,919
  • 4
  • 38
  • 59
1

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).

Chris Brandsma
  • 11,666
  • 5
  • 47
  • 58
  • No, unfortunately that's not a webservice. It is a simple ajax request that passes data in json to the server. Also, I'm pretty sure that there are no circular references. You're right that in the example there's no breed property but in my real code all properties are there. Thanks for the suggestions though :) – Tamas Czinege Jun 03 '09 at 17:09
  • On the JavaScript side of things: how are you converting the JavaScript object to JSON? – Chris Brandsma Jun 03 '09 at 17:31