System.Text.Json 使用构造函数进行反序列化

huangapple go评论107阅读模式
英文:

System.Text.Json Deserialization using constructor

问题

我正在使用 System.Text.Json 进行反序列化。

我想使用 SerialNo(string serialNo) 构造函数来构建我的对象。

    public class SerialNo
    {
        [JsonConstructor]
        public SerialNo(string serialNo)
        {
            if (serialNo == null) throw new ArgumentNullException(nameof(serialNo));

            if (string.IsNullOrWhiteSpace(serialNo)) throw new Exception("My exception text");

            Value = serialNo.Trim('0');
        }

        public string Value { get; set; }
    }

    public class Item
    {
        public SerialNo SerialNo { get; set; }

        public string AffiliationOrgCode { get; set; }
    }

    public class Root
    {
        public List<Item> Item { get; set; }
    }

    public class DeserializationTestsWithSystemTextJson
    {
        private const string JsonString = @"
            {
                ""item"": [
                  {
                    ""serialNo"": ""000000000002200878"",
                    ""affiliationOrgCode"": ""OrgCode1""
                  },
                  {
                    ""serialNo"": ""000000000002201675"",
                    ""affiliationOrgCode"": ""OrgCode1""
                  }
                ]
            }";

        [Fact]
        public void Simple_Deserialization_With_SystemTextJson()
        {
            var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
            var deserializedClass = JsonSerializer.Deserialize<Root>(JsonString, options);

            Assert.NotNull(deserializedClass);
            Assert.Equal("2201675", deserializedClass.Item[1].SerialNo.Value);
        }
    }
英文:

I am using System.Text.Json for deserialization.

I want to use the constructor in SerialNo(string serialNo) to build my object.

public class SerialNo
{
    [JsonConstructor]
    public SerialNo(string serialNo)
    {
        if (serialNo == null) throw new ArgumentNullException(nameof(serialNo));

        if (string.IsNullOrWhiteSpace(serialNo)) throw new Exception(&quot;My exception text&quot;);

        Value = serialNo.Trim(&#39;0&#39;);
    }

    public string Value { get; set; }
}

public class Item
{
    public SerialNo SerialNo { get; set; }

    public string AffiliationOrgCode { get; set; }
}

public class Root
{
    public List&lt;Item&gt; Item { get; set; }
}

public class DeserializationTestsWithSystemTextJson
{
    private const string JsonString = @&quot;
        {
            &quot;&quot;item&quot;&quot;: [
              {
                &quot;&quot;serialNo&quot;&quot;: &quot;&quot;000000000002200878&quot;&quot;,
                &quot;&quot;affiliationOrgCode&quot;&quot;: &quot;&quot;OrgCode1&quot;&quot;
              },
              {
                &quot;&quot;serialNo&quot;&quot;: &quot;&quot;000000000002201675&quot;&quot;,
                &quot;&quot;affiliationOrgCode&quot;&quot;: &quot;&quot;OrgCode1&quot;&quot;
              }
            ]
        }
    &quot;;

    [Fact]
    public void Simple_Deserialization_With_SystemTextJson()
    {
        var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
        var deserializedClass = JsonSerializer.Deserialize&lt;Root&gt;(JsonString, options);

        Assert.NotNull(deserializedClass);
        Assert.Equal(&quot;2201675&quot;, deserializedClass.Item[1].SerialNo.Value);
    }
}

Fails with:

> The JSON value could not be converted to JsonNetDeserialization.Test.WithSystemText.SerialNo. Path: $.item[0].serialNo | LineNumber: 3 | BytePositionInLine: 44.

Any ideas?

答案1

得分: 5

由于你正在将 string 转换为 SerialNo,你需要一个自定义转换器。例如:

public class SerialNoConverter : JsonConverter<SerialNo>
{
    public override SerialNo? Read(ref Utf8JsonReader reader, Type typeToConvert, 
        JsonSerializerOptions options)
    {
        var serialNo = reader.GetString();
        return new SerialNo(serialNo);
    }

    public override void Write(Utf8JsonWriter writer, SerialNo value, 
        JsonSerializerOptions options)
    {
        // 如果需要,留给 OP 处理
        throw new NotImplementedException();
    }
}

然后,你可以通过两种方式使用它,要么为你的类添加属性:

public class Item
{
    [JsonConverter(typeof(SerialNoConverter))]
    public SerialNo SerialNo { get; set; }

    public string AffiliationOrgCode { get; set; }
}

要么将转换器添加到你的序列化选项:

options.Converters.Add(new SerialNoConverter());

这里是一个运行示例

英文:

Since you are converting a string to a SerialNo, you need a custom converter. For example:

public class SerialNoConverter : JsonConverter&lt;SerialNo&gt;
{
	public override SerialNo? Read(ref Utf8JsonReader reader, Type typeToConvert, 
        JsonSerializerOptions options)
	{
		var serialNo = reader.GetString();
		return new SerialNo(serialNo);
	}

	public override void Write(Utf8JsonWriter writer, SerialNo value, 
        JsonSerializerOptions options)
	{
        // Left for OP if required
		throw new NotImplementedException();
	}
}

And to use it you can either add an attribute to your class:

public class Item
{
	[JsonConverter(typeof(SerialNoConverter))]
	public SerialNo SerialNo { get; set; }

	public string AffiliationOrgCode { get; set; }
}

Or add the converter to your serialiser options:

options.Converters.Add(new SerialNoConverter());

Here is a running example.

答案2

得分: 1

你可以使用构造函数来反序列化 JSON,使用 System.Text.Json,与标记的答案相反。甚至 Microsoft 也有相关文档:https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/immutability?pivots=dotnet-7-0

不过有一些缺点:

  1. 所有类/结构字段/属性必须与 JSON 属性名称完全匹配
  2. 所有 JSON 属性不能嵌套在更深的 JSON 对象中

要使用构造函数反序列化 JSON,只需使用 [JsonConstructor]。以下是以此方式进行反序列化的示例:

JSON:

{
  "Fp": 2.199,
  "Pi": 3.14159,
  "Str": "Hello World"
}

反序列化后的类:

public class Dto
{
    public double Fp { get; set; }
    public double Pi { get; set; }
    public string Str { get; set; }

    [JsonConstructor]
    public Dto(double fp, double pi, string str)
    {
        Fp = fp;
        Pi = pi;
        Str = str;
    }

    public override string ToString()
    {
        return $"{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}";
    }
}

现在,要进行反序列化,只需包括这个表达式:var dto = JsonSerializer.Deserialize<Dto>(Json); 如果您尝试反序列化自定义类型,可以在您的反序列化类上的自定义类型属性/字段上包括 [JsonConverter(...)] 属性,如果自定义类型没有为其类已经具有 JsonConverter 属性的话。由于类属性使用与 Json 中相同的名称,因此所有其他类型仍然会正确反序列化,而自定义类型属性将使用具有属性的自定义转换器。以下是使用自定义转换器的示例:

public class Dto
{
    public double Fp { get; set; }
    public double Pi { get; set; }
    [JsonConverter(typeof(CustomStringConverter))] // 如果 CustomString 类已经有 JsonConverter 属性,则可以将其删除
    public CustomString Str { get; set; }
    
    [JsonConstructor]
    public Dto(double fp, double pi, CustomString str)
    {
        this.Fp = fp;
        Pi = pi;
        Str = str;
    }

    public override string ToString()
    {
        return $"{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}";
    }
}
英文:

You are able to use a constructor to deserialize JSON using System.Text.Json, contrary to what the marked answer says. Even Microsoft documents it: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/immutability?pivots=dotnet-7-0

There are a some drawbacks though:

  1. All class/struct fields/properties must match the JSON property name exactly
  2. All json properties must not be nested in a deeper JSON object

To deserialize JSON with a constructor, you just need to use [JsonConstructor]. Here's an example of deserializing this way:

JSON:

{
  &quot;Fp&quot;: 2.199,
  &quot;Pi&quot;: 3.14159,
  &quot;Str&quot;: &quot;Hello World&quot;
}

The deserialized class:

public class Dto
{
    public double Fp { get; set; }
    public double Pi { get; set; }
    public string Str { get; set; }

    [JsonConstructor]
    public Dto(double fp, double pi, string str)
    {
        Fp = fp;
        Pi = pi;
        Str = str;
    }

    public override string ToString()
    {
        return $&quot;{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}&quot;;
    }
}

Now to deserialize, you just include this expression: var dto = JsonSerializer.Deserialize&lt;Dto&gt;(Json); In the event you are trying to deserialize are custom type, you can include the [JsonConverter(...)] attribute over the custom type properties/fields in your deserialized class if the custom types do not already have a JsonConverter attribute for their class. Because the class properties use the exact name as in the Json, all other types are still deserialized properly and the custom type property will use the attributed custom converter. Here is an example of using a custom converter:

public class Dto
{
    public double Fp { get; set; }
    public double Pi { get; set; }
    [JsonConverter(typeof(CustomStringConverter))] // This can be removed if the CustomString class already has a JsonConverter attribute
    public CustomString Str { get; set; }
    
    [JsonConstructor]
    public Dto(double fp, double pi, CustomString str)
    {
        this.Fp = fp;
        Pi = pi;
        Str = str;
    }

    public override string ToString()
    {
        return $&quot;{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}&quot;;
    }
}

huangapple
  • 本文由 发表于 2023年2月6日 19:25:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75360682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定