使用JObject.FromObject时的id字段分辨率



我有一系列自定义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解析。我该怎么做?

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

作为一种替代方案,您可以使用相同的序列化程序进行递归调用以进行序列化,并使转换器使用线程静态下推堆栈禁用自身,方法类似于在返回到客户端之前修改JSON的Generic方法和JSON.Net在使用[JsonConvert()]时抛出StackOverflowException。您需要增强这些示例中的转换器,以便通过使用JsonSerializer.ReferenceResolver属性手动检查并添加必要的"$id""$ref"属性。

然而,由于转换器只是添加属性,因此更直接的解决方案可能是创建一个自定义合约解析器,允许类型自定义其合约,因为它是通过应用于类型的属性中声明的回调方法生成的,例如:

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"属性和引用处理属性都存在。小提琴原型。

最新更新