如何使用$ ref和$ id在RestKit中使用循环引用

时间:2014-01-15 00:28:36

标签: ios json json.net restkit circular-reference

我有一个使用json.net序列化库的API。它使用$ ref和$ id字段进行循环引用。 RestKit没有意识到这些$ ref字段正在引用另一个已经被序列化的对象。

有没有办法让RestKit使用这些字段,因此不会创建空对象?

以下是我的json的一个例子:

{
      "$id":"1",
      "WorkOrder":{
         "$id":"2",
         "Location":{
            "$id":"3",
            "Address":{
               "$id":"4",
               "Guid":"8086990f-13a0-4f93-8a9b-043ff247ae66"
            },
            "WorkOrders":[
               {
                  "$ref":"2"
               }
            ],
            "Guid":"ae58698d-4fcf-4c31-82bf-529077b6d059"
         },
         "Appointments":[
            {
               "$ref":"1"
            }
         ],
         "Guid":"94140fc6-9885-4395-a79d-2b60452f2bf4",
      },
      "Calendar":{
         "$id":"5",
         "OwnerID":"1bbda60d-0bda-4b97-b6e5-24460106bc54",
         "IsActive":true,
         "Appointments":[
            {
               "$ref":"1"
            }
         ],
         "Guid":"e6c91678-290d-4d12-b52f-9f6ad36dd679",
      },
      "Guid":"731f20c6-6ecb-4515-ade3-df47bc929c86",
}

2 个答案:

答案 0 :(得分:5)

答案不在于RestKit,而在于您的JSON.Net序列化。以下是问题:

  • RestKit不知道如何处理$ ref,$ id
  • 您有一个WCF服务层,它将您的对象返回到您的API层(MVC APIController)。
  • 您的API层希望将对象序列化为Json到客户端。
  • WCF需要将对象作为引用进行管理,否则将发生堆栈溢出,并且您的服务将只是将对象转移到API。
  • Api层不需要$ ref $ id引用,因为RestKit无法处理。
  • RestKit CAN处理空对象作为仅包含对象主键的引用。 RestKit将使用该对象执行Upsert,只有主键,它将看到对象已经存在,并且没有其他数据需要更新,因此它将完美地处理引用。

因此,为了实现这一点,我们需要使用WCF引用将对象从我们的服务层转移到我们的API层,然后仅使用对象的主键作为参考来创建我们自己的json序列化,而不是$ WCF使用的id $ ref crap。

所以,这里有详细信息。

首先,您将获得$ ref $ id,因为您正在调用WCF服务,其中的类使用[DataContract(IsReference=true)]属性进行修饰。 JSON.Net序列化程序将读取此属性并创建$ id,$ ref引用,如果您还没有用[JsonObject(IsReference=false)]修饰类。您需要DataContract IsReference来获取您的WCF服务,以将您的对象序列化到您的ApiController。您的ApiController现在想要将对象序列化为客户端作为Json ...因此您的对象也需要Json(IsReference = false)以防止$ id,$ ref

现在,数据模型类定义现在看起来是

[JsonObject(IsReference=false)]
[DataContract(IsReference=true)]
public class SomeClassToSerialize

DataContract来自System.Runtime.Serialization lib 和JsonObject来自NewtonSoft.Json lib

好的......这已经解决了一半。此时,我们将使用WCF引用将服务中的对象返回给我们的api。我们告诉我们的API不要使用引用,因此我们的API服务现在会因堆栈溢出而崩溃,因为它会尝试序列化循环引用。

下一步是创建一个自定义JsonConverter,它将仅使用主键呈现循环引用。在下面的示例中,我的所有对象都继承自EntityBase。所有这些的主要关键是一个名为Guid的Guid。所以,这是逻辑......

跟踪HashSet中的所有渲染对象。如果尚未渲染对象,则循环遍历每个属性并渲染对象。如果对象已被渲染,则只渲染主键(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();
        }
    }

最后,我们只需要注册我们的JsonConverter。在WebApiConfig.Register()方法中,只需注册序列化程序。

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.Converters=new List<JsonConverter>(){new GuidRefJsonConverter()};

就是这样。 API现在将仅使用主键序列化循环引用,RestKit将将其作为空的Upsert选取,并将正确映射其所有指针。

答案 1 :(得分:0)

您正在寻找foreign key connections。这允许RestKit使用现有标识符搜索托管对象,以便可以重用它们而不是创建新对象。

每个ref需要不同的对象,对象需要将ref值作为变量,变量应该设置为唯一标识符,不同的对象应该相互关联。