带有派生类的Newtonsoft Json参考回路转换器

问题描述

通常,对于派生类型的反序列化,您必须定义一个自定义转换器。我的意思是,当有一个基类- Base 和其他从 Base 派生的类时。在这种情况下,您必须定义一个自定义转换器,并使用每个派生类中的一些数据(可能是一个枚举)来知道将数据转换为哪种类型。问题是当调用此转换器作为参考值时,其表示形式类似于{"$ref":"1"},而转换器无法从中获取任何信息。

有什么想法如何处理吗?

这里是更好理解的来源:

public enum ElementType
 {
     Invalid = 0,FirstElement,SecondElement
 }

 public class SubElement
 {
     [JsonConstructor]
     private SubElement() { }
     public SubElement(string name,Element parent)
     {
         if (string.IsNullOrEmpty(name))
         {
             throw new ArgumentException("message",nameof(name));
         }

         Name = name;
         Parent = parent ?? throw new ArgumentNullException(nameof(parent));
     }
     [JsonProperty]
     public string Name { get; private set; }
     [JsonProperty]
     public Element Parent { get; private set; }
 }

 [JsonObject(IsReference = true)]
 [JsonConverter(typeof(BaseConverter))]
 public abstract class Element
 {
     [JsonConstructor]
     protected Element() { }
     public Element(string name,IList<SubElement> subelements)
     {
         Name = name;
         Subelements = subelements;
     }
     [JsonProperty]
     public string Name { get; private set; }
     [JsonProperty]
     public IList<SubElement> Subelements { get; private set; }
     public abstract ElementType Type { get; }
 }

 public class FirstElement : Element
 {
     [JsonConstructor]
     private FirstElement() { }
     public FirstElement(string name,IList<SubElement> subelements) : base(name,subelements) { }
     public override ElementType Type => ElementType.FirstElement;
 }

 public class BaseConverter : CustomCreationConverter<Element>
 {
     private ElementType elementType;
     public override object ReadJson(JsonReader reader,Type objectType,object existingValue,JsonSerializer serializer)
     {
         var obj = JObject.ReadFrom(reader);
         elementType = obj["Type"].ToObject<ElementType>();
         return base.ReadJson(obj.CreateReader(),objectType,existingValue,serializer);
     }

     public override Element Create(Type objectType)
     {
         switch (elementType)
         {
             case ElementType.FirstElement:
                 return Activator.CreateInstance(typeof(FirstElement),true) as Element;
             default:
                 throw new NotImplementedException();
         }
     }
 }

 class Program
 {
     static void Main(string[] args)
     {
         Element firstElement = new FirstElement("FirstElement",new List<SubElement>());
         firstElement.Subelements.Add(new SubElement("Subelement1",firstElement));
         firstElement.Subelements.Add(new SubElement("Subelement2",firstElement));

         string serialized = JsonConvert.SerializeObject(firstElement);
         Console.WriteLine(serialized);
         Element deserialized = JsonConvert.DeserializeObject<Element>(serialized);
     }
 }

序列化输出:

{
  "$id": "1","Type": 1,"Name": "FirstElement","Subelements": [
    {
      "Name": "Subelement1","Parent": {
        "$ref": "1"
      }
    },{
      "Name": "Subelement2","Parent": {
        "$ref": "1"
      }
    }
  ]
}

解决方法

如果有人面对相同的情况,我发现了一种解决方法,方法是在 BaseConverter 类中添加字典,该字典在 Create 中创建时按其$id映射元素。 em>方法,当当前对象是$ref时,ReadJson方法将返回对应的映射元素。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...