在JSON.NET中强制转换反序列化的接口


128

我正在尝试建立一个读取器,该读取器将从各个网站接收JSON对象(考虑信息抓取)并将其转换为C#对象。我目前正在使用JSON.NET进行反序列化过程。我遇到的问题是它不知道如何处理类中的接口级属性。所以自然的东西:

public IThingy Thing

会产生错误:

无法创建类型为IThingy的实例。类型是接口或抽象类,无法实例化。

相对于Thingy,让它成为IThingy相对重要,因为我正在处理的代码被认为是敏感的,并且单元测试非常重要。对于像Thingy这样的成熟对象,无法为原子测试脚本模拟对象。它们必须是接口。

我已经研究JSON.NET的文档已有一段时间了,而我在此站点上可以找到的与此相关的问题都来自一年多以前。有什么帮助吗?

另外,如果有关系,我的应用程序是用.NET 4.0编写的。


Answers:


115

@SamualDavis在一个相关的问题中提供了一个很好的解决方案,我将在这里进行总结。

如果必须将JSON流反序列化为具有接口属性的具体类,则可以将具体类作为参数包含在该类的构造函数中! NewtonSoft反序列化器足够聪明,可以弄清楚它需要使用那些具体的类来反序列化属性。

这是一个例子:

public class Visit : IVisit
{
    /// <summary>
    /// This constructor is required for the JSON deserializer to be able
    /// to identify concrete classes to use when deserializing the interface properties.
    /// </summary>
    public Visit(MyLocation location, Guest guest)
    {
        Location = location;
        Guest = guest;
    }
    public long VisitId { get; set; }
    public ILocation Location { get;  set; }
    public DateTime VisitDate { get; set; }
    public IGuest Guest { get; set; }
}

15
如何使用ICollection?ICollection <IGuest>客人{get; set;}
DrSammyD 2014年

12
它可以与ICollection <ConcreteClass>一起使用,因此ICollection <Guest>可以使用。与FYI一样,您可以在构造函数上放置属性[JsonConstructor],以便在碰巧有多个构造函数的情况下默认使用该属性
DrSammyD 2014年

6
我遇到了同样的问题,在我的情况下,我对接口进行了几种实现(在您的示例中,接口为ILocation),如果有MyLocation,VIPLocation,OrdinaryLocation之类的类怎么办。如何将这些映射到Location属性?如果只有MyLocation这样的实现,这很容易,但是如果ILocation有多个实现,怎么办呢?
ATHER

10
如果您有多个构造函数,则可以使用[JsonConstructor]属性标记特殊的构造函数。
罗伯·

26
这根本不好。使用接口的目的是使用依赖注入,但是使用构造函数所需的对象类型参数来执行此操作,则完全弄糟了将接口作为属性的观点。
杰罗姆MEVEL

57

(从此问题复制)

如果我无法控制传入的JSON(因此无法确保它包含$ type属性),我将编写一个自定义转换器,该转换器仅允许您显式指定具体类型:

public class Model
{
    [JsonConverter(typeof(ConcreteTypeConverter<Something>))]
    public ISomething TheThing { get; set; }
}

它仅使用Json.Net的默认序列化器实现,同时显式指定具体类型。

概述可在此博客文章中找到。源代码如下:

public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        //assume we can convert to anything for now
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        //explicitly specify the concrete type we want to create
        return serializer.Deserialize<TConcrete>(reader);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //use the default serialization - it works fine
        serializer.Serialize(writer, value);
    }
}

11
我真的很喜欢这种方法,并将其应用于我们自己的项目。我什至添加了一个ConcreteListTypeConverter<TInterface, TImplementation>处理type的类成员IList<TInterface>
奥利弗(Oliver)

3
那是很多代码。concreteTypeConverter不过,在问题中提供实际代码可能会更好。
克里斯(Chris)

2
@Oliver-您可以发布ConcreteListTypeConverter<TInterface, TImplementation>实现吗?
迈克尔(Michael)

2
而且,如果您有ISomething的两个实现者?
bdaniel7 '19

56

为什么要使用转换器?有一个本机功能Newtonsoft.Json可以解决此确切问题:

设置TypeNameHandlingJsonSerializerSettingsTypeNameHandling.Auto

JsonConvert.SerializeObject(
  toSerialize,
  new JsonSerializerSettings()
  {
    TypeNameHandling = TypeNameHandling.Auto
  });

这会将每种类型放入json中,而不是作为类型的具体实例保存,而是作为接口或抽象类保存。

确保对序列化和反序列化使用相同的设置

我对其进行了测试,即使使用列表,它也一样具有魅力。

搜索结果Web结果与站点链接

⚠️ 警告

仅将其用于来自已知和可信来源的json。用户snipsnipsnip正确地提到这确实是一个漏洞。

有关更多信息,请参见CA2328SCS0028


源码和替代的手动实现:Code Inside Blog


3
完美,这帮助我快速和肮脏的深克隆(stackoverflow.com/questions/78536/deep-cloning-objects
Compufreak

1
@Shimmy对象:“序列化为JSON对象结构时,请包括.NET类型名称。” 自动:当要序列化的对象的类型与其声明的类型不同时,请包括.NET类型名称。请注意,默认情况下,这不包括根序列化的对象。要在JSON你必须指定SerializeObject(对象,类型,JsonSerializerSettings)或序列化(JsonWriter,对象,类型)“源根型对象的根对象的类型名称:newtonsoft.com/json/help/html/...
马菲

4
我只是在反序列化上尝试过此方法,但它不起作用。这个Stack Overflow问题的主题行是“在JSON.NET中铸造反序列化的接口”
Justin Russo

3
@JustinRusso它仅在使用相同设置对json进行序列化时才有效
Mafii

3
支持快速解决方案,即使该解决方案不脏的话。如果您只是序列化配置,则可以使用。击败停止开发以构建转换器的过程,当然击败装饰每个注入的属性。serializer.TypeNameHandling = TypeNameHandling.Auto; JsonConvert.DefaultSettings()。TypeNameHandling = TypeNameHandling.Auto;
肖恩·安德森

39

要启用接口的多个实现的反序列化,可以使用JsonConverter,但不能通过属性:

Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new DTOJsonConverter());
Interfaces.IEntity entity = serializer.Deserialize(jsonReader);

DTOJsonConverter用具体的实现映射每个接口:

class DTOJsonConverter : Newtonsoft.Json.JsonConverter
{
    private static readonly string ISCALAR_FULLNAME = typeof(Interfaces.IScalar).FullName;
    private static readonly string IENTITY_FULLNAME = typeof(Interfaces.IEntity).FullName;


    public override bool CanConvert(Type objectType)
    {
        if (objectType.FullName == ISCALAR_FULLNAME
            || objectType.FullName == IENTITY_FULLNAME)
        {
            return true;
        }
        return false;
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        if (objectType.FullName == ISCALAR_FULLNAME)
            return serializer.Deserialize(reader, typeof(DTO.ClientScalar));
        else if (objectType.FullName == IENTITY_FULLNAME)
            return serializer.Deserialize(reader, typeof(DTO.ClientEntity));

        throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
    }

    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

只有解串器才需要DTOJsonConverter。序列化过程保持不变。Json对象不需要嵌入具体的类型名称。

这样的SO post通过通用JsonConverter进一步提供了相同的解决方案。


WriteJson方法对serializer.Serialize的调用是否会导致堆栈溢出,因为对转换器要序列化的值进行调用serialize会导致递归地再次调用转换器的WriteJson方法?
Triynko

如果CanConvert()方法返回一致的结果,则不应该这样。
埃里克·布孟迪尔

3
FullName当您可以直接比较类型时,为什么要比较?
Alex Zhukovskiy

仅比较类型也可以。
埃里克·布孟迪尔

23

使用此类将抽象类型映射为实类型:

public class AbstractConverter<TReal, TAbstract> : JsonConverter where TReal : TAbstract
{
    public override Boolean CanConvert(Type objectType) 
        => objectType == typeof(TAbstract);

    public override Object ReadJson(JsonReader reader, Type type, Object value, JsonSerializer jser) 
        => jser.Deserialize<TReal>(reader);

    public override void WriteJson(JsonWriter writer, Object value, JsonSerializer jser) 
        => jser.Serialize(writer, value);
}

...反序列化时:

        var settings = new JsonSerializerSettings
        {
            Converters = {
                new AbstractConverter<Thing, IThingy>(),
                new AbstractConverter<Thing2, IThingy2>()
            },
        };

        JsonConvert.DeserializeObject(json, type, settings);

1
我真的很喜欢一个简洁的答案,可以解决我的问题。不需要autofac或任何东西!
Ben Power

3
值得将其放入转换器类声明中:where TReal : TAbstract确保将其转换为类型
Artemious

1
可能更完整where TReal : class, TAbstract, new()
Erik Philips

2
我也将此转换器与struct一起使用,我相信“ TReal:TAbstract”就足够了。谢谢大家
吉尔多

2
金!光滑的路要走。
SwissCoder

12

Nicholas Westby在一篇很棒的文章中提供了一个很好的解决方案。

如果要将JSON反序列化为实现这样的接口的许多可能的类之一:

public class Person
{
    public IProfession Profession { get; set; }
}

public interface IProfession
{
    string JobTitle { get; }
}

public class Programming : IProfession
{
    public string JobTitle => "Software Developer";
    public string FavoriteLanguage { get; set; }
}

public class Writing : IProfession
{
    public string JobTitle => "Copywriter";
    public string FavoriteWord { get; set; }
}

public class Samples
{
    public static Person GetProgrammer()
    {
        return new Person()
        {
            Profession = new Programming()
            {
                FavoriteLanguage = "C#"
            }
        };
    }
}

您可以使用自定义JSON转换器:

public class ProfessionConverter : JsonConverter
{
    public override bool CanWrite => false;
    public override bool CanRead => true;
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(IProfession);
    }
    public override void WriteJson(JsonWriter writer,
        object value, JsonSerializer serializer)
    {
        throw new InvalidOperationException("Use default serialization.");
    }

    public override object ReadJson(JsonReader reader,
        Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var profession = default(IProfession);
        switch (jsonObject["JobTitle"].Value())
        {
            case "Software Developer":
                profession = new Programming();
                break;
            case "Copywriter":
                profession = new Writing();
                break;
        }
        serializer.Populate(jsonObject.CreateReader(), profession);
        return profession;
    }
}

您将需要使用JsonConverter属性装饰“ Profession”属性,以使其知道使用自定义转换器:

    public class Person
    {
        [JsonConverter(typeof(ProfessionConverter))]
        public IProfession Profession { get; set; }
    }

然后,您可以使用接口强制转换类:

Person person = JsonConvert.DeserializeObject<Person>(jsonString);

8

您可以尝试两件事:

实施尝试/解析模型:

public class Organisation {
  public string Name { get; set; }

  [JsonConverter(typeof(RichDudeConverter))]
  public IPerson Owner { get; set; }
}

public interface IPerson {
  string Name { get; set; }
}

public class Tycoon : IPerson {
  public string Name { get; set; }
}

public class Magnate : IPerson {
  public string Name { get; set; }
  public string IndustryName { get; set; }
}

public class Heir: IPerson {
  public string Name { get; set; }
  public IPerson Benefactor { get; set; }
}

public class RichDudeConverter : JsonConverter
{
  public override bool CanConvert(Type objectType)
  {
    return (objectType == typeof(IPerson));
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    // pseudo-code
    object richDude = serializer.Deserialize<Heir>(reader);

    if (richDude == null)
    {
        richDude = serializer.Deserialize<Magnate>(reader);
    }

    if (richDude == null)
    {
        richDude = serializer.Deserialize<Tycoon>(reader);
    }

    return richDude;
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}

或者,如果可以在对象模型中这样做,请在IPerson和叶对象之间实现一个具体的基类,然后反序列化它。

第一个可能在运行时失败,第二个需要更改您的对象模型,并将输出同质化为最小公分母。


由于我必须使用规模,因此尝试/解析模型不可行。我必须考虑数百个基础对象的范围以及甚至更多的存根/帮助器对象,以表示发生很多事情的嵌入式JSON对象。更改对象模型不是没有问题的,但是在属性中使用具体的基类是否会使我们无法模拟用于单元测试的项目?还是我以某种方式落后了?
tmesser 2011年

您仍然可以从IPerson实现模拟-请注意,Organisation.Owner属性的类型仍为IPerson。但是,要对任意目标进行反序列化,您必须返回一个具体类型。如果您不拥有类型定义,并且无法定义代码所需的最少属性集,那么您的最后选择就是使用键/值袋。使用您的Facebook示例注释-您可以在答案中发布您的(一个或多个)ILocation实现是什么样的吗?这可能有助于推动事情发展。
mcw

由于主要希望是模拟,所以ILocation接口实际上只是Location具体对象的外观。我刚刚处理的一个简单示例将是针对界面的类似(pastebin.com/mWQtqGnB)和针对具体对象的如此(pastebin.com/TdJ6cqWV)。
tmesser 2011年

下一步,这是IPage(pastebin.com/iuGifQXp)和Page(pastebin.com/ebqLxzvm)的示例。当然,问题在于尽管反序列化页面通常可以很好地工作,但是当它到达ILocation属性时会窒息。
tmesser 2011年

好的,考虑一下您实际上要抓取和反序列化的对象-通常情况下,JSON数据与单个具体的类定义一致吗?意味着(假设),您不会遇到带有附加属性的“位置”,这些附加属性会使Location不适合用作反序列化对象的具体类型吗?如果是这样,应使用“ LocationConverter”为Page的ILocation属性赋值。如果不是,那是因为JSON数据并不总是符合严格或一致的结构(例如ILocation),那么(...继续)
mcw

8

我发现这很有用。你可能也是。

用法示例

public class Parent
{
    [JsonConverter(typeof(InterfaceConverter<IChildModel, ChildModel>))]
    IChildModel Child { get; set; }
}

自定义创建转换器

public class InterfaceConverter<TInterface, TConcrete> : CustomCreationConverter<TInterface>
    where TConcrete : TInterface, new()
{
    public override TInterface Create(Type objectType)
    {
        return new TConcrete();
    }
}

Json.NET文档


1
不是可行的解决方案。不处理列表,导致到处散布装饰器/注释。
肖恩·安德森

5

对于那些可能对Oliver引用的ConcreteListTypeConverter感到好奇的人,这是我的尝试:

public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface 
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var res = serializer.Deserialize<List<TImplementation>>(reader);
        return res.ConvertAll(x => (TInterface) x);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

1
我对被覆盖感到困惑CanConvert(Type objectType) { return true;}。似乎很客气,这有什么用呢?我可能是错的,但这是否不是像告诉一个经验不足的小型战斗机,无论对手是谁,他们都将赢得这场战斗?
Chef_Code

4

对于它的价值,我最终不得不自己去处理。每个对象都有一个Deserialize(string jsonStream)方法。它的一些摘要:

JObject parsedJson = this.ParseJson(jsonStream);
object thingyObjectJson = (object)parsedJson["thing"];
this.Thing = new Thingy(Convert.ToString(thingyObjectJson));

在这种情况下,新的Thingy(string)是一个构造函数,它将调用适当具体类型的Deserialize(string jsonStream)方法。在您到达json.NET可以处理的基本点之前,此方案将继续下降。

this.Name = (string)parsedJson["name"];
this.CreatedTime = DateTime.Parse((string)parsedJson["created_time"]);

等等等等。此安装程序使我能够处理json.NET安装程序,而不必重构库本身的很大一部分,也不必使用笨拙的try / parse模型,由于涉及的对象数量过多,它们会使整个库陷入瘫痪。这也意味着我可以有效地处理特定对象上的所有json更改,而无需担心该对象涉及的所有内容。这绝不是理想的解决方案,但从我们的单元测试和集成测试来看,它的效果很好。


4

假设如下所示的autofac设置:

public class AutofacContractResolver : DefaultContractResolver
{
    private readonly IContainer _container;

    public AutofacContractResolver(IContainer container)
    {
        _container = container;
    }

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

        // use Autofac to create types that have been registered with it
        if (_container.IsRegistered(objectType))
        {
           contract.DefaultCreator = () => _container.Resolve(objectType);
        }  

        return contract;
    }
}

然后,假设您的课程是这样的:

public class TaskController
{
    private readonly ITaskRepository _repository;
    private readonly ILogger _logger;

    public TaskController(ITaskRepository repository, ILogger logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public ITaskRepository Repository
    {
        get { return _repository; }
    }

    public ILogger Logger
    {
        get { return _logger; }
    }
}

因此,反序列化中解析器的用法可能类似于:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();

IContainer container = builder.Build();

AutofacContractResolver contractResolver = new AutofacContractResolver(container);

string json = @"{
      'Logger': {
        'Level':'Debug'
      }
}";

// ITaskRespository and ILogger constructor parameters are injected by Autofac 
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
    ContractResolver = contractResolver
});

Console.WriteLine(controller.Repository.GetType().Name);

您可以在http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm中查看更多详细信息


我会将此投票作为最佳解决方案。如今,DI已被c#Web开发人员广泛使用,它非常适合作为解析器处理这些类型转换的集中场所。
appletwo

3

没有对象将永远 一个IThingy的接口都被定义为抽象的。

您首先被序列化的对象是某种具体类型,实现了抽象接口。您需要具有相同的具体类来恢复序列化的数据。

然后,生成的对象将具有某种类型,可以实现您要查找的抽象接口。

根据文档,您可以使用

(Thingy)JsonConvert.DeserializeObject(jsonString, typeof(Thingy));

反序列化以通知JSON.NET有关具体类型时。


这恰好是我一年前提到的帖子。就我被迫考虑的规模而言,唯一的主要建议(编写自定义转换器)并不可行。在随后的一年中,JSON.NET发生了很大变化。我完全理解类和接口之间的区别,但是C#还支持从接口到实现有关类型的接口的对象的隐式转换。我实质上是在问是否有办法告诉JSON.NET哪个对象将实现此接口。
tmesser 2011年

我指出的答案就是所有这些。确保存在_type指示使用的具体类型的属性。
肖恩·金西

而且我强烈怀疑C#是否支持从声明为接口的变量到具体类型的任何“隐式”类型转换,而没有任何提示。
肖恩·金西

除非我看错,否则_type属性应该在要序列化的JSON中。如果您仅反序列化已经序列化的内容,那会很好,但这不是这里要发生的事情。我从许多不会遵循该标准的站点中提取JSON。
tmesser 2011年

@YYY-您是否控制源JSON的序列化和反序列化?因为最终您需要将具体类型嵌入序列化的JSON中以作为反序列化时使用的提示,或者您需要使用某种try / parse模型来在运行时检测/尝试检测具体类型并调用适当的反序列化器。
mcw

3

我对此的解决方案很受欢迎,我喜欢它的解决方法如下:

/// <summary>
/// Automagically convert known interfaces to (specific) concrete classes on deserialisation
/// </summary>
public class WithMocksJsonConverter : JsonConverter
{
    /// <summary>
    /// The interfaces I know how to instantiate mapped to the classes with which I shall instantiate them, as a Dictionary.
    /// </summary>
    private readonly Dictionary<Type,Type> conversions = new Dictionary<Type,Type>() { 
        { typeof(IOne), typeof(MockOne) },
        { typeof(ITwo), typeof(MockTwo) },
        { typeof(IThree), typeof(MockThree) },
        { typeof(IFour), typeof(MockFour) }
    };

    /// <summary>
    /// Can I convert an object of this type?
    /// </summary>
    /// <param name="objectType">The type under consideration</param>
    /// <returns>True if I can convert the type under consideration, else false.</returns>
    public override bool CanConvert(Type objectType)
    {
        return conversions.Keys.Contains(objectType);
    }

    /// <summary>
    /// Attempt to read an object of the specified type from this reader.
    /// </summary>
    /// <param name="reader">The reader from which I read.</param>
    /// <param name="objectType">The type of object I'm trying to read, anticipated to be one I can convert.</param>
    /// <param name="existingValue">The existing value of the object being read.</param>
    /// <param name="serializer">The serializer invoking this request.</param>
    /// <returns>An object of the type into which I convert the specified objectType.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return serializer.Deserialize(reader, this.conversions[objectType]);
        }
        catch (Exception)
        {
            throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
        }
    }

    /// <summary>
    /// Not yet implemented.
    /// </summary>
    /// <param name="writer">The writer to which I would write.</param>
    /// <param name="value">The value I am attempting to write.</param>
    /// <param name="serializer">the serializer invoking this request.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

}

您显然可以通过添加一个构造函数(将其实例化为Dictionary <Type,Type>类型的参数来实例化转换实例变量)来将其转换为更通用的转换器。


3

几年后,我遇到了类似的问题。在我的情况下,存在大量嵌套的接口,并且偏好在运行时生成具体类,以便它可以与泛型类一起使用。

我决定在运行时创建一个代理类,该类包装Newtonsoft返回的对象。

这种方法的优点是它不需要类的具体实现,并且可以自动处理任何深度的嵌套接口。您可以在我的博客上看到更多有关它的信息。

using Castle.DynamicProxy;
using Newtonsoft.Json.Linq;
using System;
using System.Reflection;

namespace LL.Utilities.Std.Json
{
    public static class JObjectExtension
    {
        private static ProxyGenerator _generator = new ProxyGenerator();

        public static dynamic toProxy(this JObject targetObject, Type interfaceType) 
        {
            return _generator.CreateInterfaceProxyWithoutTarget(interfaceType, new JObjectInterceptor(targetObject));
        }

        public static InterfaceType toProxy<InterfaceType>(this JObject targetObject)
        {

            return toProxy(targetObject, typeof(InterfaceType));
        }
    }

    [Serializable]
    public class JObjectInterceptor : IInterceptor
    {
        private JObject _target;

        public JObjectInterceptor(JObject target)
        {
            _target = target;
        }
        public void Intercept(IInvocation invocation)
        {

            var methodName = invocation.Method.Name;
            if(invocation.Method.IsSpecialName && methodName.StartsWith("get_"))
            {
                var returnType = invocation.Method.ReturnType;
                methodName = methodName.Substring(4);

                if (_target == null || _target[methodName] == null)
                {
                    if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
                    {

                        invocation.ReturnValue = null;
                        return;
                    }

                }

                if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
                {
                    invocation.ReturnValue = _target[methodName].ToObject(returnType);
                }
                else
                {
                    invocation.ReturnValue = ((JObject)_target[methodName]).toProxy(returnType);
                }
            }
            else
            {
                throw new NotImplementedException("Only get accessors are implemented in proxy");
            }

        }
    }



}

用法:

var jObj = JObject.Parse(input);
InterfaceType proxyObject = jObj.toProxy<InterfaceType>();

谢谢!这是正确支持动态类型(鸭子类型)而不强制限制传入json的唯一答案。
菲利普·皮特尔

没问题。看到那里什么都没有,我感到有些惊讶。自从最初的示例以来,它已经有所发展,所以我决定共享代码。github.com/sudsy/JsonDuckTyper。我还在nuget上将其发布为JsonDuckTyper。如果您想增强它,只需给我发送PR,我将很乐意为您服务。
Sudsy

当我在这方面寻找解决方案时,我也遇到了github.com/ekonbenefits/impromptu-interface。对于我来说,它不起作用,因为它不支持dotnet core 1.0,但可能对您有用。
Sudsy

我确实尝试过使用Impromptu接口,但是Json.Net不愿意PopulateObject在Impromptu接口生成的代理上进行操作。不幸的是,我放弃了Duck Typing的工作-创建自定义Json Contract Serializer更加容易,该自定义Json Contract Serializer使用反射来查找所请求接口的现有实现并使用它。
菲利普·皮特尔

1

使用此JsonKnownTypes,它的使用方法非常相似,它只是向json添加了鉴别

[JsonConverter(typeof(JsonKnownTypeConverter<Interface1>))]
[JsonKnownType(typeof(MyClass), "myClass")]
public interface Interface1
{  }
public class MyClass : Interface1
{
    public string Something;
}

现在,当你在序列化JSON对象将增加"$type""myClass"价值,这将是使用了反序列化

杰森:

{"Something":"something", "$type":"derived"}

0

我的解决方案在构造函数中添加了接口元素。

public class Customer: ICustomer{
     public Customer(Details details){
          Details = details;
     }

     [JsonProperty("Details",NullValueHnadling = NullValueHandling.Ignore)]
     public IDetails Details {get; set;}
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.