The answer is not RestKit, but your JSON.Net Serialization. Here are the problems:
- RestKit does not know what to do with $ ref, $ id
- You have a WCF service level that returns your objects to your API level (MVC APIController).
- Your API layer wants to serialize objects as Json for the client.
- WCF You want to manage objects as links or, and your service will try to simply pass API objects.
- The Api level does not require $ ref $ id links, since RestKit cannot handle this.
- RestKit CAN treats an empty object as a link containing only the primary key of the object. RestKit will perform Upsert with this object, having only the primary key, it will see that the object already exists and does not have other data to update, so it will handle this link perfectly.
So, for this we need to get objects from our service level to our API level using WCF links, and then create our own json serialization using only the primary key of the object as a link, and not the $ id $ ref that WCF uses.
So here are the details.
First of all, you get your $ ref $ id, because you call the WCF service, whose classes are decorated with the [DataContract(IsReference=true)] attribute. The JSON.Net serializer will read this attribute and create links $ id, $ ref, if you also did not decorate your classes with [JsonObject(IsReference=false)] . You need a DataContract IsReference for your WCF service to serialize your objects to your ApiController. Now your ApiController wants to serialize objects to the client as Json ... so your objects also need Json (IsReference = false) to prevent $ id, $ ref
So, here is what a data model class definition looks like
[JsonObject(IsReference=false)] [DataContract(IsReference=true)] public class SomeClassToSerialize
DataContract - from System.Runtime.Serialization lib and JsonObject from NewtonSoft.Json lib
Ok ... this is a half solution. At this point, we will return the objects from the service back to our api using WCF links. And we told our API NOT to use links, so our API service will now be broken with a stack overflow, as it will try to serialize circular links.
The next step is to create a custom JsonConverter that will display circular references using only the primary key. In my example below, all my objects are inherited from EntityBase. And the main key to all of this is Guid, called Guid. So here is the logic ...
Keep track of all rendered objects in a HashSet. If the object has not been visualized, swipe through each property and visualize the object. If the HAS object was rendered, then print only the primary key (Guid).
public class GuidRefJsonConverter : JsonConverter { public override bool CanRead { get { return false; } } public override bool CanWrite { get { return true; } } public override bool CanConvert(Type objectType) { return typeof(EntityBase).IsAssignableFrom(objectType); } private HashSet<EntityBase> serializedObjects = new HashSet<EntityBase>(); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { EntityBase eb = (EntityBase)value; JObject jo = new JObject(); jo.Add("Guid", eb.Guid.ToString()); if (serializedObjects.Add(eb)) { foreach (PropertyInfo prop in value.GetType().GetProperties()) { if (prop.GetCustomAttribute<JsonIgnoreAttribute>() != null) continue; if (prop.CanRead) { object propVal = prop.GetValue(value); if (propVal != null && prop.Name!="Guid") { jo.Add(prop.Name, JToken.FromObject(propVal, serializer)); } } } } jo.WriteTo(writer); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } }
Finally, we just need to register our JsonConverter. In the WebApiConfig.Register () method, simply register the serializer.
var json = config.Formatters.JsonFormatter; json.SerializerSettings.Converters=new List<JsonConverter>(){new GuidRefJsonConverter()};
and it does. The API will now serialize the circular reference with only the primary key, which RestKit will accept as an empty Upsert, and correctly display all its pointers.