使用System.Text.Json序列化实现接口的对象

时间:2019-10-14 09:32:35

标签: json .net-core-3.0 system.text.json

我有一个包含通用集合的主类。集合中的元素具有不同的类型,每个元素都实现一个接口。

大师班:

public class MasterClass
{
    public ICollection<IElement> ElementCollection { get; set; }
}

合同内容:

public interface IElement
{
    string Key { get; set; }
}

元素的两个样本:

public class ElementA : IElement
{
    public string Key { get; set; }

    public string AValue { get; set; }
}

public class ElementB : IElement
{
    public string Key { get; set; }

    public string BValue { get; set; }
}

我需要使用Json中新的MasterClass库序列化System.Text.Json对象的实例。使用以下代码,

public string Serialize(MasterClass masterClass)
{
    var options = new JsonSerializerOptions
    {
        WriteIndented = true,
    };
    return JsonSerializer.Serialize(masterClass, options);
}

我得到了以下JSON:

{
    "ElementCollection":
    [
        {
            "Key": "myElementAKey1"
        },
        {
            "Key": "myElementAKey2"
        },
        {
            "Key": "myElementBKey1"
        }
    ]
}

代替:

{
    "ElementCollection":
    [
        {
            "Key": "myElementAKey1",
            "AValue": "MyValueA-1"
        },
        {
            "Key": "myElementAKey2",
            "AValue": "MyValueA-2"
        },
        {
            "Key": "myElementBKey1",
            "AValue": "MyValueB-1"
        }
    ]
}

我应该实现哪个类(转换器,作家等)以获取完整的JSON?

预先感谢您的帮助。

4 个答案:

答案 0 :(得分:2)

这对我有用:

public class TypeMappingConverter<TType, TImplementation> : JsonConverter<TType>
  where TImplementation : TType
{
  [return: MaybeNull]
  public override TType Read(
    ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
      JsonSerializer.Deserialize<TImplementation>(ref reader, options);

  public override void Write(
    Utf8JsonWriter writer, TType value, JsonSerializerOptions options) =>
      JsonSerializer.Serialize(writer, (TImplementation)value!, options);
}

用法:

var options =
   new JsonSerializerOptions 
   {
     Converters = 
     {
       new TypeMappingConverter<BaseType, ImplementationType>() 
     }
   };

JsonSerializer.Deserialize<Wrapper>(value, options);

测试:

[Fact]
public void Should_serialize_references()
{
  // arrange
  var inputEntity = new Entity
  {
    References =
    {
      new Reference
      {
        MyProperty = "abcd"
      },
      new Reference
      {
        MyProperty = "abcd"
      }
    }
  };

  var options = new JsonSerializerOptions
  {
    WriteIndented = true,
    Converters =
    {
      new TypeMappingConverter<IReference, Reference>()
    }
  };

      var expectedOutput =
@"{
  ""References"": [
    {
      ""MyProperty"": ""abcd""
    },
    {
      ""MyProperty"": ""abcd""
    }
  ]
}";

  // act
  var actualOutput = JsonSerializer.Serialize(inputEntity, options);

  // assert
  Assert.Equal(expectedOutput, actualOutput);
}

[Fact]
public void Should_deserialize_references()
{
  // arrange

  var inputJson =
@"{
  ""References"": [
    {
      ""MyProperty"": ""abcd""
    },
    {
      ""MyProperty"": ""abcd""
    }
  ]
}";

  var expectedOutput = new Entity
  {
    References =
    {
      new Reference
      {
        MyProperty = "abcd"
      },
      new Reference
      {
        MyProperty = "abcd"
      }
    }
  };

  var options = new JsonSerializerOptions
  {
    WriteIndented = true
  };

  options.Converters.AddTypeMapping<IReference, Reference>();

  // act
  var actualOutput = JsonSerializer.Deserialize<Entity>(inputJson, options);

  // assert
  actualOutput
      .Should()
      .BeEquivalentTo(expectedOutput);
}


public class Entity
{
  HashSet<IReference>? _References;
  public ICollection<IReference> References
  {
    get => _References ??= new HashSet<IReference>();
    set => _References = value?.ToHashSet();
  }
}

public interface IReference
{
  public string? MyProperty { get; set; }
}

public class Reference : IReference
{
  public string? MyProperty { get; set; }
}

答案 1 :(得分:1)

我遇到了同样的问题,但我的问题可能与您的问题无关。事实证明,必须将传入的 JSON 数据序列化到的每个对象都需要一个没有参数的构造函数。我所有的对象都有带有所有参数的构造函数(以便更容易地从数据库中创建和填充它们)。

答案 2 :(得分:0)

解决方案是实现通用转换器(System.Text.Json.Serialization.JsonConverter):

public class ElementConverter : JsonConverter<IElement>
{
    public override IElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }

    public override void Write(Utf8JsonWriter writer, IElement value, JsonSerializerOptions options)
    {
        if (value is ElementA)
            JsonSerializer.Serialize(writer, value as ElementA, typeof(ElementA), options);
        else if (value is ElementB)
            JsonSerializer.Serialize(writer, value as ElementB, typeof(ElementB), options);
        else
            throw new ArgumentOutOfRangeException(nameof(value), $"Unknown implementation of the interface {nameof(IElement)} for the parameter {nameof(value)}. Unknown implementation: {value?.GetType().Name}");
    }
}

Read方法只需要加总工作量。

答案 3 :(得分:0)

您要查找的被称为多态序列化

Here's Microsoft documentation article

Here's another question about it

根据文档,您只需要将接口转换为对象即可。 例如:

public class TreeRow
{
    [JsonIgnore]
    public ICell[] Groups { get; set; } = new ICell[0];

    [JsonIgnore]
    public ICell[] Aggregates { get; set; } = new ICell[0];

    [JsonPropertyName("Groups")]
    public object[] JsonGroups => Groups;

    [JsonPropertyName("Aggregates")]
    public object[] JsonAggregates => Aggregates;


    public TreeRow[] Children { get; set; } = new TreeRow[0];
}