gpt4 book ai didi

c# - 如何在 .net core 3.0 中使用 System.Text.Json 反序列化部分 json?

转载 作者:行者123 更新时间:2023-12-03 23:40:08 25 4
gpt4 key购买 nike

我有一个来自这里的 json https://api.nasa.gov/insight_weather/?api_key=DEMO_KEY&feedtype=json&ver=1.0看起来像:

{
"782": {
"First_UTC": "2021-02-06T17:08:11Z",
"Last_UTC": "2021-02-07T17:47:46Z",
"Month_ordinal": 12,
"Northern_season": "late winter",
"PRE": {
"av": 721.77,
"ct": 113450,
"mn": 698.8193,
"mx": 742.2686
},
"Season": "winter",
"Southern_season": "late summer",
"WD": {
"most_common": null
}
},
"783": {
"First_UTC": "2021-02-07T17:47:46Z",
"Last_UTC": "2021-02-08T18:27:22Z",
"Month_ordinal": 12,
"Northern_season": "late winter",
"PRE": {
"av": 722.186,
"ct": 107270,
"mn": 698.7664,
"mx": 743.1983
},
"Season": "winter",
"Southern_season": "late summer",
"WD": {
"most_common": null
}
},
"sol_keys": [ "782", "783" ],
"validity_checks": { /* Some complex object */ }
}
我只需要部分信息,所以我创建了以下类:
public class MarsWheather {
[JsonPropertyName("First_UTC")]
public DateTime FirstUTC { get; set; }
[JsonPropertyName("Last_UTC")]
public DateTime LastUTC { get; set; }
[JsonPropertyName("Season")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public Season MarsSeason { get; set; }
[JsonPropertyName("PRE")]
public DataDescription AtmosphericPressure { get; set; }
}

public enum Season {
winter,
spring,
summer,
autumn
}

public class DataDescription{
[JsonPropertyName("av")]
public double Average { get; set; }
[JsonPropertyName("ct")]
public double TotalCount { get; set; }
[JsonPropertyName("mn")]
public double Minimum { get; set; }
[JsonPropertyName("mx")]
public double Maximum { get; set; }
}
问题是来自 NASA 的 JSON 根对象包含属性 "validity_checks""sol_keys"我不需要并且想跳过。在 Newton.Json 中,我使用了 JObject.Parse这样做,但在 System.Text.Json我想用
JsonSerializer.DeserializeAsync<Dictionary<string, MarsWheather>>(stream, new JsonSerializerOptions { IgnoreNullValues = true });
不幸的是,当我这样做时,我得到了一个异常(exception):
System.Text.Json.JsonException: The JSON value could not be converted to MarsWheather. Path: $.sol_keys | LineNumber: 120 | BytePositionInLine: 15.

演示 fiddle here .
是否可以?

最佳答案

您的 JSON 根对象由某些固定键( "sol_keys""validity_checks" )组成,它们的值每个都有一些固定模式,以及任意数量的变量键("782" 数字键),它们的值都共享一个不同于固定键值的模式:

{
"782": {
// Properties corresponding to your MarsWheather object
},
"783": {
// Properties corresponding to your MarsWheather object
},
// Other variable numeric key/value pairs corresponding to KeyValuePair<string, MarsWheather>
"sol_keys": [
// Some array values you don't care about
],
"validity_checks": {
// Some object you don't care about
}
}
您只想反序列化变量键,但是当您尝试反序列化为 Dictionary<string, MarsWheather>你会得到一个异常,因为序列化程序试图反序列化一个固定键值,就好像它是变量键值一样——但是由于固定键有一个数组值,而变量键有对象值,所以会抛出异常。怎么可能 System.Text.Json被告知跳过已知的固定键而不是尝试反序列化它们?
如果您只想反序列化变量键并跳过固定的已知键,您需要创建一个 custom JsonConverter .最简单的方法是首先为您的字典创建一些根对象:
[JsonConverter(typeof(MarsWheatherRootObjectConverter))]
public class MarsWheatherRootObject
{
public Dictionary<string, MarsWheather> MarsWheathers { get; } = new Dictionary<string, MarsWheather>();
}
然后为它定义如下转换器:
public class MarsWheatherRootObjectConverter : FixedAndvariablePropertyNameObjectConverter<MarsWheatherRootObject, Dictionary<string, MarsWheather>, MarsWheather>
{
static readonly Dictionary<string, ReadFixedKeyMethod> FixedKeyReadMethods = new Dictionary<string, ReadFixedKeyMethod>(StringComparer.OrdinalIgnoreCase)
{
{ "sol_keys", (ref Utf8JsonReader reader, MarsWheatherRootObject obj, string name, JsonSerializerOptions options) => reader.Skip() },
{ "validity_checks", (ref Utf8JsonReader reader, MarsWheatherRootObject obj, string name, JsonSerializerOptions options) => reader.Skip() },
};

protected override Dictionary<string, MarsWheather> GetDictionary(MarsWheatherRootObject obj) => obj.MarsWheathers;
protected override void SetDictionary(MarsWheatherRootObject obj, Dictionary<string, MarsWheather> dictionary) => throw new RowNotInTableException();
protected override bool TryGetFixedKeyReadMethod(string name, JsonSerializerOptions options, out ReadFixedKeyMethod method) => FixedKeyReadMethods.TryGetValue(name, out method);
protected override IEnumerable<KeyValuePair<string, WriteFixedKeyMethod>> GetFixedKeyWriteMethods(JsonSerializerOptions options) => Enumerable.Empty<KeyValuePair<string, WriteFixedKeyMethod>>();
}

public abstract class FixedAndvariablePropertyNameObjectConverter<TObject, TDictionary, TValue> : JsonConverter<TObject>
where TDictionary : class, IDictionary<string, TValue>, new()
where TObject : new()
{
protected delegate void ReadFixedKeyMethod(ref Utf8JsonReader reader, TObject obj, string name, JsonSerializerOptions options);
protected delegate void WriteFixedKeyMethod(Utf8JsonWriter writer, TObject value, JsonSerializerOptions options);

protected abstract TDictionary GetDictionary(TObject obj);
protected abstract void SetDictionary(TObject obj, TDictionary dictionary);
protected abstract bool TryGetFixedKeyReadMethod(string name, JsonSerializerOptions options, out ReadFixedKeyMethod method);
protected abstract IEnumerable<KeyValuePair<string, WriteFixedKeyMethod>> GetFixedKeyWriteMethods(JsonSerializerOptions options);

public override TObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return (typeToConvert.IsValueType && Nullable.GetUnderlyingType(typeToConvert) == null)
? throw new JsonException(string.Format("Unepected token {0}", reader.TokenType))
: default(TObject);
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException(string.Format("Unepected token {0}", reader.TokenType));
var obj = new TObject();
var dictionary = GetDictionary(obj);
var valueConverter = (typeof(TValue) == typeof(object) ? null : (JsonConverter<TValue>)options.GetConverter(typeof(TValue))); // Encountered a bug using the builtin ObjectConverter
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
var name = reader.GetString();
reader.ReadAndAssert();
if (TryGetFixedKeyReadMethod(name, options, out var method))
{
method(ref reader, obj, name, options);
}
else
{
if (dictionary == null)
SetDictionary(obj, dictionary = new TDictionary());
dictionary.Add(name, valueConverter.ReadOrDeserialize(ref reader, typeof(TValue), options));
}
}
else if (reader.TokenType == JsonTokenType.EndObject)
{
return obj;
}
else
{
throw new JsonException(string.Format("Unepected token {0}", reader.TokenType));
}
}
throw new JsonException(); // Truncated file
}

public override void Write(Utf8JsonWriter writer, TObject value, JsonSerializerOptions options)
{
writer.WriteStartObject();
var dictionary = GetDictionary(value);
if (dictionary != null)
{
var valueConverter = (typeof(TValue) == typeof(object) ? null : (JsonConverter<TValue>)options.GetConverter(typeof(TValue))); // Encountered a bug using the builtin ObjectConverter
foreach (var pair in dictionary)
{
// TODO: handle DictionaryKeyPolicy
writer.WritePropertyName(pair.Key);
valueConverter.WriteOrSerialize(writer, pair.Value, typeof(TValue), options);
}
}
foreach (var pair in GetFixedKeyWriteMethods(options))
{
writer.WritePropertyName(pair.Key);
pair.Value(writer, value, options);
}
writer.WriteEndObject();
}
}

public static partial class JsonExtensions
{
public static void WriteOrSerialize<T>(this JsonConverter<T> converter, Utf8JsonWriter writer, T value, Type type, JsonSerializerOptions options)
{
if (converter != null)
converter.Write(writer, value, options);
else
JsonSerializer.Serialize(writer, value, type, options);
}

public static T ReadOrDeserialize<T>(this JsonConverter<T> converter, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> converter != null ? converter.Read(ref reader, typeToConvert, options) : (T)JsonSerializer.Deserialize(ref reader, typeToConvert, options);

public static void ReadAndAssert(this ref Utf8JsonReader reader)
{
if (!reader.Read())
throw new JsonException();
}
}
现在您将能够反序列化为 MarsWheatherRootObject如下:
var root = await System.Text.Json.JsonSerializer.DeserializeAsync<MarsWheatherRootObject>(
stream,
new System.Text.Json.JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
演示 fiddle #1 here .
笔记:
  • FixedAndvariablePropertyNameObjectConverter<TObject, TDictionary, TValue>为序列化和反序列化具有固定和可变属性的对象提供了一个通用框架。如果稍后您决定反序列化,例如"sol_keys" , 你可以修改 MarsWheatherRootObject如下:
    [JsonConverter(typeof(MarsWheatherRootObjectConverter))]
    public class MarsWheatherRootObject
    {
    public Dictionary<string, MarsWheather> MarsWheathers { get; } = new Dictionary<string, MarsWheather>();
    public List<string> SolKeys { get; set; } = new List<string>();
    }
    转换器如下:
    public class MarsWheatherRootObjectConverter : FixedAndvariablePropertyNameObjectConverter<MarsWheatherRootObject, Dictionary<string, MarsWheather>, MarsWheather>
    {
    static readonly Dictionary<string, ReadFixedKeyMethod> FixedKeyReadMethods = new(StringComparer.OrdinalIgnoreCase)
    {
    { "sol_keys", (ref Utf8JsonReader reader, MarsWheatherRootObject obj, string name, JsonSerializerOptions options) =>
    {
    obj.SolKeys = JsonSerializer.Deserialize<List<string>>(ref reader, options);
    }
    },
    { "validity_checks", (ref Utf8JsonReader reader, MarsWheatherRootObject obj, string name, JsonSerializerOptions options) => reader.Skip() },
    };
    static readonly Dictionary<string, WriteFixedKeyMethod> FixedKeyWriteMethods = new Dictionary<string, WriteFixedKeyMethod>()
    {
    { "sol_keys", (w, v, o) =>
    {
    JsonSerializer.Serialize(w, v.SolKeys, o);
    }
    },
    };

    protected override Dictionary<string, MarsWheather> GetDictionary(MarsWheatherRootObject obj) => obj.MarsWheathers;
    protected override void SetDictionary(MarsWheatherRootObject obj, Dictionary<string, MarsWheather> dictionary) => throw new RowNotInTableException();
    protected override bool TryGetFixedKeyReadMethod(string name, JsonSerializerOptions options, out ReadFixedKeyMethod method) => FixedKeyReadMethods.TryGetValue(name, out method);
    protected override IEnumerable<KeyValuePair<string, WriteFixedKeyMethod>> GetFixedKeyWriteMethods(JsonSerializerOptions options) => FixedKeyWriteMethods;
    }
    演示 fiddle #2 here .
  • 关于c# - 如何在 .net core 3.0 中使用 System.Text.Json 反序列化部分 json?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66258830/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com