C# Newtonsoft:如何在没有 Json 属性的情况下反序列化包含接口的接口

C# Newtonsoft: how do deserialize interface containing interface containing interface, without Json attributes

提问人:Markus 提问时间:10/6/2023 更新时间:10/7/2023 访问量:99

问:

我有以下代码,它在最后一行中断,出现异常“无法创建 DeserializeInterfaces.IC 类型的实例”。如何在不使用任何属性的情况下反序列化,例如:

[JsonProperty(ItemConverterType = typeof(ConcreteConverter<C>))]`  

我不能使用这些属性,因为接口在不同的项目中,我不能在具体类上建立接口的循环依赖关系。

我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace DeserializeInterfaces
{
  public class CustomInterfaceConverter : JsonConverter
  {
    public override bool CanConvert( Type objectType )
    {
      bool convertible = typeof( IB ).IsAssignableFrom( objectType )
                         || typeof( IC ).IsAssignableFrom( objectType );

      return convertible;
    }

    public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
    {
      JObject jsonObject = JObject.Load( reader );
          
      if (objectType.Name == "IB")
      {
        B retVal = jsonObject.ToObject<B>();
        return retVal;
      }
      if (objectType.Name == "IC")
      {
        C retVal = jsonObject.ToObject<C>();
        return retVal;
      }
      // Handle other cases or return a default implementation
      return null;
    }
    public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
    {
      throw new NotImplementedException();
    }
  }

  public interface IA
  {
    IB b { get; set; }
  }

  public class A : IA
  {
    public A() { }

    public IB b { get; set; }
  }

  public interface IB
  {
    IC c { get; set; }
  }


  public class B : IB
  {
    public B() { }

    public B( IC pC )
    {
      c = pC;
    }

    public IC c { get; set; }
  }

  public interface IC
  {
    int v { get; set; }
  }

  public class C : IC
  {
    public C() { }

    public C( int pV )
    {
      v = pV;
    }

    public int v { get; set; }
  }

  class Program
  {
    static void Main( string[] args )
    {
      CustomInterfaceConverter jsonConverter = new CustomInterfaceConverter();
      IA a = new A();
      a.b = new B();
      a.b.c = new C();
      a.b.c.v = 1;
      string aSer = JsonConvert.SerializeObject( a );

      A aDeser = JsonConvert.DeserializeObject<A>( aSer, jsonConverter );
    }
  }
}
嵌套 json.net 反序列化的 C# 接口

评论

0赞 Serge 10/6/2023
如果不发布 json 字符串,您肯定不会获得任何帮助
0赞 dbc 10/7/2023
这与您 3 小时前提出的问题有何不同?C# Newtonsoft:如何反序列化包含接口的接口,而不使用 Json 属性
1赞 Alexei Levenkov 10/7/2023
@Serge代码确实生成了 JSON 字符串...您能否澄清一下为什么您认为这还不够?
0赞 Markus 10/8/2023
该问题针对的是包含接口的接口。这是包含接口的接口包含接口。

答:

2赞 dbc 10/7/2023 #1

假设应始终反序列化为 、as 和 as ,您可以使用三个实例 of 来反序列化 JSON,每个接口一个,其中转换器从这个答案中获取 Json.Net - 对基于接口的数据结构进行性能反序列化?IAAIBBICCInterfaceToConcreteConverter<TInterface, TConcrete>

public class InterfaceToConcreteConverter<TInterface, TConcrete> : JsonConverter where TConcrete : TInterface
{
    static InterfaceToConcreteConverter()
    {
        // TConcrete should be a subtype of an abstract type, or an implementation of an interface.  If they
        // are identical an infinite recursion could result, so throw an exception.
        if (typeof(TInterface) == typeof(TConcrete))
            throw new InvalidOperationException(string.Format("typeof({0}) == typeof({1})", typeof(TInterface), typeof(TConcrete)));
    }

    public override bool CanConvert(Type objectType) =>objectType == typeof(TInterface);
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => serializer.Deserialize(reader, typeof(TConcrete));
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

使用此转换器,序列化代码将如下所示:

var settings = new JsonSerializerSettings
{
    Converters = { new InterfaceToConcreteConverter<IA, A>(), 
                  new InterfaceToConcreteConverter<IB, B>(),
                  new InterfaceToConcreteConverter<IC, C>(),}
};

string aSer = JsonConvert.SerializeObject( a, settings );

A aDeser = JsonConvert.DeserializeObject<A>( aSer, settings );

笔记:

  • 这个答案取决于每个接口都有一个且只有一个具体实现的事实。如果这不正确,则需要使用某种以前序列化的类型标识符来确定具体类型。

  • 单一责任原则是你的朋友。创建一个通用接口到具体转换器并实例化三个实例比为所有接口创建一些综合转换器要容易得多。

    如果有许多不同的位置需要使用转换器进行序列化或反序列化,则可以创建静态默认设置以供项目范围使用:

    public static class JsonExtensions
    {
        public static JsonSerializerSettings DefaultSettings { get; } =
            new JsonSerializerSettings
            {
                Converters = { new InterfaceToConcreteConverter<IA, A>(), 
                              new InterfaceToConcreteConverter<IB, B>(),
                              new InterfaceToConcreteConverter<IC, C>(),}
            };
    }
    
    A aDeser = JsonConvert.DeserializeObject<A>( aSer, JsonExtensions.DefaultSettings );
    
  • 通过从 CanWrite 返回,写入时将使用默认序列化。false

演示小提琴在这里

0赞 Alexei Levenkov 10/7/2023 #2

您需要将序列化程序传递给所有调用,以便它可以递归调用转换器(而不是使用 default):ToObject

B retVal = jsonObject.ToObject<B>(serializer);

评论

0赞 Markus 10/8/2023
我试过了,但得到了无限递归到 JsonConverter 中
0赞 Serge 10/7/2023 #3

您可以在类属性(而不是接口)上使用这些特性。由于此转换器不使用任何接口,因此它不需要每个接口都有一个且只有一个具体实现(因为@dbc解决方案需要)

A aDeser = JsonConvert.DeserializeObject<A>(aSer);

public class CustomInterfaceConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken o = JToken.Load(reader);
        return o.ToObject(Type.GetType((string)o["TypeName"]));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType) => objectType == typeof(AType);
    public override bool CanWrite => false;
}

public abstract class AType
{
    public string TypeName
    {
        get { return this.GetType().Name; }
    }
}

public class A : AType, IA
{
    public A() { }
    [JsonConverter(typeof(CustomInterfaceConverter))]
    public IB b { get; set; }
}
public class B : AType, IB
{
    public B() { }

    public B(IC pC)
    {
        c = pC;
    }
    [JsonConverter(typeof(CustomInterfaceConverter))]
    public IC c { get; set; }
}
public class C : AType, IC
{
    public C() { }

    public C(int pV)
    {
        v = pV;
    }

    public int v { get; set; }
}