System.Text.Json:在自定义转换器中获取属性名称

问题描述 投票:0回答:3

使用

JsonSerialize.DeserializeAsync
和自定义转换器进行反序列化,例如

public class MyStringJsonConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return reader.GetString();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

在这里我会得到 all

string
属性,这还可以,不过有没有办法检查给定值的属性名称,例如像这样的东西,在哪里只处理
Body
属性:

class MyMailContent 
{
    public string Name { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}

public class MyStringJsonConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.PropertyName.Equals("Body"))
        {
            var s = reader.GetString();
            //do some process with the string value
            return s;
        }

        return reader.GetString();
    }
}

或者还有其他方法来挑选给定的属性吗?

注意,我正在寻找使用

System.Text.Json
的解决方案。

c# json-deserialization system.text.json asp.net-core-5.0
3个回答
5
投票

感谢Jimi评论,并在他的批准下,我使用他建议的解决方案发布了一个维基答案,任何有更多贡献的人,请随时这样做。

在大多数情况下,接受的答案是一个好的答案,但如果仍然需要通过属性名称处理一个或多个特定属性,这里是一种方法。

public class MyMailContent
{
    public string Name { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}

public class MyMailContentJsonConverter : JsonConverter<MyMailContent>
{
    public override MyMailContent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException();
        }

        var mailContent = new MyMailContent();

        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
            {
                return mailContent;
            }

            if (reader.TokenType == JsonTokenType.PropertyName)
            {
                var propertyName = reader.GetString();

                reader.Read();

                switch (propertyName)
                {
                    case "Name":
                        mailContent.Name = reader.GetString();
                        break;

                    case "Subject":
                        mailContent.Subject = reader.GetString();
                        break;

                    case "Body":
                        string body = reader.GetString();
                        //do some process on body here
                        mailContent.Body = body;
                        break;
                }
            }
        }

        throw new JsonException();
    }

    public override void Write(Utf8JsonWriter writer, MyMailContent mailcontent, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

private static JsonSerializerOptions _jsonDeserializeOptions = new()
{
    ReadCommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true,
    Converters =
    {
        new MyMailContentJsonConverter()
    }
};

然后像这样使用它

var jsonstring = JsonSerializer.Serialize(new MyMailContent
{
    Name = "some name",
    Subject = "some subject",
    Body = "some body"
});

var MailContent = JsonSerializer.Deserialize<MyMailContent>(jsonstring, _jsonDeserializeOptions);

4
投票

System.Text.Json 不会在 JsonConverter<T>.Read() 中提供父属性名称,或者更一般地说,当前值的

路径
。此信息在内部跟踪 - 它位于
ReadStack.JsonPath()
中 - 但
ReadStack
是内部的,从未传递给应用程序代码。

但是,如注册示例 - 属性上的 [JsonConverter] 中所述,您可以使用

MyStringJsonConverter
public string Body { get; set; }
 直接应用于 
JsonConverterAttribute
:

class MyMailContent 
{
    public string Name { get; set; }
    public string Subject { get; set; }
    [JsonConverter(typeof(MyStringJsonConverter))]
    public string Body { get; set; }
}

通过这样做,

MyStringJsonConverter.Read()
.Write()
将仅在
MyMailContent.Body
时触发。即使您在
JsonConverter<string>
 中有一些整体 
JsonSerializerOptions.Converters
,应用于该属性的转换器也会 优先:

在序列化或反序列化期间,按以下顺序为每个 JSON 元素选择转换器,从最高优先级到最低优先级列出:

  • [JsonConverter]
    应用于属性。
  • 转换器已添加到
    Converters
    集合中。
  • [JsonConverter]
    应用于自定义值类型或 POCO。

(请注意,这与 Newtonsoft 部分不同,其中应用于类型的转换器取代设置中的转换器。)


0
投票

如果您想在多个属性上使用自定义

JsonConverter
并且想知道 Read/Write 方法在哪个属性上触发,还有另一种解决方案。您需要自定义
JsonConverter
和自定义
JsonConverterAttribute
类。你需要像这样实现它:

  public class StringTypeJsonConverter : JsonConverter<string>
  {
    readonly string _propertyName;

    public StringTypeJsonConverter (string propertyName) : base()
    {
      _propertyName = propertyName;
    }

    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
      //////////////////////////////////////
      // do something with your _propertyName
      //////////////////////////////////////

      return reader.GetString()
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
      //////////////////////////////////////
      // do something with your _propertyName
      //////////////////////////////////////

      writer.WriteString(value);
    }
  }
    
  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
  public class StringJsonConverterAttribute: JsonConverterAttribute
  {
    readonly string _propertyName;

    public StringJsonConverterAttribute(string propertyName) : base() 
    {
      _propertyName = propertyName;
    }

    public override JsonConverter? CreateConverter(Type typeToConvert)
    {
      return new StringJsonConverterAttribute(_propertyName);
    }
  }

当你获得这一部分后,你所需要的就是在你的字符串属性上使用这个属性:

class MyMailContent 
{
    [StringJsonConverter(nameof(Name))]
    public string Name { get; set; }

    [StringJsonConverter(nameof(Subject))]
    public string Subject { get; set; }

    [StringJsonConverter(nameof(Body))]
    public string Body { get; set; }
}
© www.soinside.com 2019 - 2024. All rights reserved.