7
votes

I am trying with no luck to deserialize object graph with circular reference,

Json.net works well when serializing circular references : adding a $id to objects and replacing objects with $ref = *objectId, When i send the same data back to the MVC action, it won't deserialize correctly - replacing the $refs with empty objects.

I use json.net to both serialize and deserialize, deserializing is implemented in a custom IValueProvider - https://json.codeplex.com/discussions/347099

I noticed that when deserializing to an anonymous object

JsonConverter.Deserialize(json);

it will not deal with the circular references. but when deserializing using a generic type

JsonConverter.Deserialize<EntityType>(json) 

it will deserialize correctly.

But i can't find the type in the GetValueProvider(ControllerContext controllerContext)

any help will be appreciated

Edit- In my current fix i pass the whole json as a string and use

JsonConverter.Deserialize<EntityType>(json) 

with the right type in the controller's action, but that's definitely not the right way to work with json + mvc4... I need a better way to integrate it into mvc, started a bounty

Edit- More code The type :

[JsonObject(IsReference = true)]
public class DynamicEntity : EntityWithID<Guid>
{
    ....

    public virtual IList<DynamicEntity> ReferenceFields { get; set; }
}

The json to deserialize is the output of the Serialize method of Json.net.

{"$id":"1","ReferenceFields":[{"$ref":"1"}],"Id":"9f9de7f3-865e-4511-aeac-a2ff01193b06"}

The issue is the integration with MVC because the json goes back and forth between the server and client. I already have js methods to change it back and forth to the same exact format - tested as i use it like this for now :

public ActionResult EntitySaveOrUpdate(string entity)
    {
        var entityToSave = JsonConvert.DeserializeObject<DynamicEntity>(entity);
        ...
    }

And it works just fine, but i need a better integration with MVC and not deserialize in all of my actions...

2
As far as I've been taught circular references are bad and will give unexpected behavior, but perhaps i don't understand the question. Have to stepped into the code when sending the data back to mvc to see what it is before deserializationmatt_lethargic
The data is fine, in the custom ValueProvider in MVC (link in the question) there's a Deserialize, Json.Net's Deserialize works well only when you pass it the actual type - which i cannot find in the controllerContext. as to circular references - with inverse relationships you can't avoid them.Royi Mindel
Can you output some of the JSON so we can see the format of it and the circular record. Can you also show the code for an EntityType it would deserialize to?Haney
I'm a little confused. Is your issue the circular reference (which doesn't seem to be the case), or that you want to de-serialize to anonymous rather than defined type (which seems to be real question)?Dave Alperovich
My issue is the integration of the circular reference behaviour of Json.Net (needing the typed deserialize) with MVC's ValueProvider/ModelBinder patternRoyi Mindel

2 Answers

2
votes
The challenge is dynamically typing at compile-time an un-typed languages (JSON).

Since JSON is actually Java-Script Object Notation, and JavaScript is weakly typed (or un-typed), the compile-time interpreter isn't sure if "1" refers to an int, double, or string type.

Notice, you don't get the same problem when you use a DynamicEntity, because the Run-Time is allowed to interpret in similar manner to JavaScript.

  • This post of how dynamics and static-anonymous-types treat deserialization is a little old, but still relevant. Their approach is to use JsonConvert.DeserializeAnonymousType() -- a dynamic method -- to return anonymous.

  • This post expands on Web Api's DataContractJsonSerializer, and how it limits your options with anonymous types. The answer suggested is to

"to replace the MediaTypeFormatter and plug in our own JSON serializer"

This amounts to a lot of work for your limited needs.

What you'll find is, short of using Linq projection, your DynamicEntity is probably as good approach as you will find.

1
votes

Did you try to write JsonConverter and register it in your ValueProviderFactory.

JSONSerializer.Converters.Add(new DynamicEntityObjectConverter());

And inside your converter you can manualy use JsonConverter.Deserialize<EntityType>(json) which works for circular reference.