具有嵌套对象的自定义JSON.Net JSON转换器

x33g5p2x  于 2022-12-01  发布在  .NET
关注(0)|答案(2)|浏览(132)

展示问题的源代码:https://github.com/Snuffsis/ConverterExample

因此,我有一个问题,它与stackoverflow问题中的问题完全相同:
C# Newtonsoft.Json Custom Deserializer
虽然这个答案对于简单类型的属性(int,bool,string等)是有帮助的,但当需要嵌套对象时,它就不起作用了。因为它抛出了Newtonsoft.Json.JsonSerializationException: Self referecing loop detected for property 'Value' with type ...的异常,其中类型是json对象,在本例中是soBillingContact。
它应该能够处理这两种JSON格式
示例:
第一个
如果我将对象创建为根,那么关联问题中的解决方案对于对象本身是有效的,只有当它是嵌套对象时,它才成为问题。
我试图避免为每个存在的json对象编写一个自定义的转换器,而是尝试创建一个通用的转换器。这可能是我的问题,也许应该放弃。但只是检查是否有人可能有任何解决方案的想法。
除了上面的解决方案,我还编写了自己的转换器,可以做类似的事情,工作很好。
这是我自己做的代码,当它为一个特定的对象:Main

static void Main(string[] args)
{
  var vSalesOrder = new SalesOrder()
  {
    Project = 1,
    PrintDescriptionOnInvoice = true,
    PrintNoteOnExternalDocuments = true,
    SoBillingContact = new Contact
    {
      Attention = "attention",
      Email = "@whatever.se",
      Fax = "lolfax"
    }
  };

  var jsonString = JsonConvert.SerializeObject(vSalesOrder);
}

除了一些被遗漏的属性之外,这之后的预期输出应该与上面的json具有类似的结构。
SalesOrder类别:
WrapWithValueConverter代码可以在顶部的链接溢出问题中找到。

public class SalesOrder
{
  [JsonProperty("project", NullValueHandling = NullValueHandling.Ignore)]
  [JsonConverter(typeof(WrapWithValueConverter<int?>))]
  public int? Project { get; set; }
  
  [JsonProperty("printDescriptionOnInvoice", NullValueHandling = NullValueHandling.Ignore)]
  [JsonConverter(typeof(WrapWithValueConverter<bool>))]
  public bool PrintDescriptionOnInvoice { get; set; }

  [JsonProperty("printNoteOnExternalDocuments", NullValueHandling = NullValueHandling.Ignore)]
  [JsonConverter(typeof(WrapWithValueConverter<bool>))]
  public bool PrintNoteOnExternalDocuments { get; set; }

  [JsonProperty("printNoteOnInternalDocuments", NullValueHandling = NullValueHandling.Ignore)]
  [JsonConverter(typeof(WrapWithValueConverter<bool>))]
  public bool PrintNoteOnInternalDocuments { get; set; }

  [JsonProperty("soBillingContact", NullValueHandling = NullValueHandling.Ignore)]
  [JsonConverter(typeof(ContactDtoJsonConverter))]
  public Contact SoBillingContact { get; set; }
}

ContactDtoJsonConverter类别:

public class ContactDtoJsonConverter : JsonConverter<Contact>
{
    public override bool CanRead => false;

    public override bool CanWrite => true;

    public override Contact ReadJson(JsonReader reader, Type objectType, Contact existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, Contact value, JsonSerializer serializer)
    {
        var dtoContact = new DtoContact
        {
            Value = value
        };
        JToken t = JToken.FromObject(dtoContact);
        JObject o = (JObject)t;

        o.WriteTo(writer);
    }
}

DtoContact类别:

public class DtoContact
{
  [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
  public Contact Value { get; set; }
}

Contact类别:

public class Contact
{
   [JsonProperty("overrideContact", NullValueHandling = NullValueHandling.Ignore)]
   public bool OverrideContact { get;set; }

   [JsonProperty("attention", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Attention { get; set; }
  
   [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Email { get; set; }
  
   [JsonProperty("fax", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Fax { get; set; }
  
   [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Name { get; set; }
  
   [JsonProperty("phone1", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Phone1 { get; set; }
  
   [JsonProperty("phone2", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Phone2 { get; set; }
  
   [JsonProperty("web", NullValueHandling = NullValueHandling.Ignore)]
   [JsonConverter(typeof(StringDtoJsonConverter))]
   public string Web { get; set; }
}

StringDtoJsonConverter类别:

public class StringDtoJsonConverter : JsonConverter<string>
{
  public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
  {
    return (string)reader.Value;
  }
  
  public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
  {
    JToken t = JToken.FromObject(value);
    if (t.Type != JTokenType.Object)
    {
      var dtoValue = new DtoString
      {
        Value = value
      };
      serializer.Serialize(writer, dtoValue);
    }
  }
}
qltillow

qltillow1#

您可以尝试以下代码,它不需要任何自定义转换器

var jsonObj = JObject.Parse(json);

    SalesOrder salesOrder = null;
    
    if (jsonObj["printNoteOnInternalDocuments"].Type == JTokenType.Boolean) 
                                                salesOrder = jsonObj.ToObject<SalesOrder>();
else
{
  var newJsonObj = new JObject
  {
  ["printNoteOnInternalDocuments"] = jsonObj["printNoteOnInternalDocuments"]["value"],

  ["soBillingContact"] = new JObject(  ((JObject) jsonObj["soBillingContact"]["value"]).Properties()
                .Select(p=> new JProperty( p.Name,p.Value["value"])))
  };

        salesOrder = newJsonObj.ToObject<SalesOrder>();
}
ogq8wdun

ogq8wdun2#

this answerC# Newtonsoft.Json Custom Deserializer 的转换器可以通过将[JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)]应用到DTO.value来修复,以避免 * Self referecing loop* 错误,如下所示:

sealed class DTO { [JsonConverter(typeof(NoConverter)), JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)] public TValue value { get; set; } public object GetValue() => value; }

请注意,执行此操作将禁用PreserveReferencesHandling
也就是说,您编写了 It should be able handle these two JSON formats。如果您事先知道需要哪种格式,处理这两种格式的最简单方法是创建一个自定义的契约解析器,在运行时应用转换器。这样做的额外好处是允许您从模型中删除所有[JsonConverter(typeof(WrapWithValueConverter<T>))]属性。
首先定义以下合同确定程序:

public class WrapWithValueContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property.Converter == null && property.ItemConverter == null) // property.Converter check is required to avoid applying the converter to WrapWithValueConverter<TValue>.DTO.value
            property.Converter = (JsonConverter)Activator.CreateInstance(typeof(WrapWithValueConverter<>).MakeGenericType(property.PropertyType));
        return property;
    }
}

public class WrapWithValueConverter<TValue> : JsonConverter
{
    // Here we take advantage of the fact that a converter applied to a property has highest precedence to avoid an infinite recursion.
    sealed class DTO { [JsonConverter(typeof(NoConverter)), JsonProperty(ReferenceLoopHandling  = ReferenceLoopHandling.Serialize)] public TValue value { get; set; } public object GetValue() => value; }

    public override bool CanConvert(Type objectType) => typeof(TValue).IsAssignableFrom(objectType);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        => serializer.Serialize(writer, new DTO { value = (TValue)value });

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        => serializer.Deserialize<DTO>(reader)?.GetValue();
}

public class NoConverter : JsonConverter
{
    // NoConverter taken from this answer https://stackoverflow.com/a/39739105/3744182
    // By https://stackoverflow.com/users/3744182/dbc
    // To https://stackoverflow.com/questions/39738714/selectively-use-default-json-converter
    public override bool CanConvert(Type objectType)  { throw new NotImplementedException(); /* This converter should only be applied via attributes */ }
    public override bool CanRead => false;
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

现在,如果您想要 Package 的格式,请使用下列设定进行序列化和还原序列化:

DefaultContractResolver resolver = new WrapWithValueContractResolver // Cache statically and reuse for best performance
{
    //NamingStrategy = new CamelCaseNamingStrategy(), // Uncomment if you need camel case
}; 

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

var order2 = JsonConvert.DeserializeObject<SalesOrder>(json, settings);

如果您不需要 Package 格式,请进行序列化和反序列化,而不必像往常一样设置解析器。
演示小提琴here

相关问题