使用JObject.FromObject时的id字段解析

时间:2016-06-11 08:28:37

标签: c# json serialization json.net

我有一系列自定义Json转换器。他们的工作方式如下:

public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) {
  JObject jo = JObject.FromObject(value);
  // do my own stuff to the JObject here -- basically adding a property. The value of the property depends on the specific converter being used.
  jo.WriteTo(writer, this);
}

这个问题是JObject的id字段总是1.不好。所以我尝试使用内部序列化器来获取id字段:

private JsonSerializer _InnerSerializer {get;set;}
private JsonSerializer InnerSerializer {
  get {
    if (_InnerSerializer == null) {
      _InnerSerializer = new JsonSerializer();
    }
    return _InnerSerializer;
  }
}

public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) {
  JsonSerializer inner = this.InnerSerializer;
  jo = JObject.FromObject(value, inner);
  //my stuff here
  jo.WriteTo(writer, this);
}

每次都会给出不同的id,即使它两次击中同一个对象。我真正想要的是使用Json通常的id解析和我的自定义序列化。我怎么能这样做?

1 个答案:

答案 0 :(得分:1)

使用内部序列化程序的想法不会按原样运行。 id-to-object映射表保存在私有字段JsonSerializerInternalBase._mappings中,无法将其从外部序列化程序复制到内部序列化程序。

作为替代方案,您可以使用相同的序列化程序进行序列化的递归调用,并使转换器使用Generic method of modifying JSON before being returned to clientJSON.Net throws StackOverflowException when using [JsonConvert()]的线程静态下推堆栈来禁用自身。 JsonSerializer.ReferenceResolver。您需要在这些示例中增强转换器,以便通过使用custom contract resolver属性手动检查并添加必要的"$id""$ref"属性。

但是,由于您的转换器只是添加属性,因此更简单的问题解决方案可能是创建fiddle,允许类型自定义合同,因为它是通过应用于属性中声明的回调方法生成的。类型,例如:

public class ModifierContractResolver : DefaultContractResolver
{
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
    static ModifierContractResolver instance;

    // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
    static ModifierContractResolver() { instance = new ModifierContractResolver(); }

    public static ModifierContractResolver Instance { get { return instance; } }

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);

        // Apply in reverse order so inherited types are applied after base types.
        foreach (var attr in objectType.GetCustomAttributes<JsonObjectContractModifierAttribute>(true).Reverse())
        {
            var modifier = (JsonObjectContractModifier)Activator.CreateInstance(attr.ContractModifierType, true);
            modifier.ModifyContract(objectType, contract);
        }
        return contract;
    }
}

public abstract class JsonObjectContractModifier
{
    public abstract void ModifyContract(Type objectType, JsonObjectContract contract);
}

[System.AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class JsonObjectContractModifierAttribute : System.Attribute
{
    private readonly Type _contractModifierType;

    public Type ContractModifierType { get { return _contractModifierType; } }

    public JsonObjectContractModifierAttribute(Type contractModifierType)
    {
        if (contractModifierType == null)
        {
            throw new ArgumentNullException("contractModifierType");
        }
        if (!typeof(JsonObjectContractModifier).IsAssignableFrom(contractModifierType))
        {
            throw new ArgumentNullException(string.Format("{0} is not a subtype of {1}", contractModifierType, typeof(JsonObjectContractModifier)));
        }
        this._contractModifierType = contractModifierType;
    }    
}

然后,将其应用于您的类型,如以下示例所示:

[JsonObjectContractModifier(typeof(TestContractModifier))]
public class Test
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

class TestContractModifier : JsonObjectContractModifier
{
    class EmptyValueProvider : IValueProvider
    {
        // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
        static EmptyValueProvider() { }

        internal static readonly EmptyValueProvider Instance = new EmptyValueProvider();

        #region IValueProvider Members

        public object GetValue(object target)
        {
            var test = target as Test;
            if (test == null)
                return null;
            return test.A == null && test.B == null && test.C == null;
        }

        public void SetValue(object target, object value)
        {
            var property = target as Test;
            if (property == null)
                return;
            if (value != null && value.GetType() == typeof(bool) && (bool)value == true)
            {
                property.A = property.B = property.C = null;
            }
        }

        #endregion
    }

    public override void ModifyContract(Type objectType, JsonObjectContract contract)
    {
        var jsonProperty = new JsonProperty
        {
            PropertyName = "isEmpty",
            UnderlyingName = "isEmpty",
            PropertyType = typeof(bool?),
            NullValueHandling = NullValueHandling.Ignore,
            Readable = true,
            Writable = true,
            DeclaringType = typeof(Test),
            ValueProvider = EmptyValueProvider.Instance,
        };

        contract.Properties.Add(jsonProperty);
    }
}

序列化如下:

var settings = new JsonSerializerSettings 
{ 
    PreserveReferencesHandling = PreserveReferencesHandling.Objects,  // Or PreserveReferencesHandling.All
    ContractResolver = ModifierContractResolver.Instance, 
};

var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);

这会生成以下JSON:

[
  {
    "$id": "1",
    "A": "hello",
    "B": "goodbye",
    "C": "sea",
    "isEmpty": false
  },
  {
    "$ref": "1"
  },
  {
    "$id": "2",
    "A": null,
    "B": null,
    "C": null,
    "isEmpty": true
  },
}

如您所见,合成"isEmpty"属性和引用处理属性都存在。原型Python documentation