如何将 json 反序列化为结构?

How Can I deserialize a json into a structure?

我有一个这样的 class 映射:

public class Settings
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("type")]
    public string Type { get; set; }

    [JsonProperty("content")]
    public ContentStructure Content { get; set; }
}


public struct ContentStructure
{
    public Content ContentClass;
    public string ContentString;

    public static implicit operator ContentStructure(Content content) => new ContentStructure { ContentClass = content };
    public static implicit operator ContentStructure(string @string) => new ContentStructure { ContentString = @string };
}


public class Content
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("duration")]
    public long Duration { get; set; }
}

当我尝试反序列化以下 JSON 字符串时:

{
    "id": "any_id",
    "type": "any_type",
    "content": {
        "id": "any_id",
        "duration": 1000
    }
}

我总是使用 属性 settings.Content.ContentClass null 获取反序列化设置对象,但每当我的 JSON 字符串具有 属性 "content" 作为字符串(而不是对象)结构字段 ContentString 正确。我做错了什么?我怎样才能正确转换上面的 JSON 字符串?

输入的 json 不符合您的格式。

如果您改用 json,

{
    "id": "any_id",
    "type": "any_type",
    "content": {
        "ContentClass" : {
            "id": "any_id",
            "duration": 1000
        }
    }
}

这样就可以了。

settings.Content.ContentClass 是三层,而你的是 json 两层 (settings.Content)。所以在“content”之后,它正在寻找 ContentStructure 没有这两个字段的 id 和 duration。

我的推理是当它遇到一个值类型(如{“field”:“value}”)时,它会寻找值类型,如string,int或double。当它遇到json类型时(如 {"field" : {another json here} }),它将查找 class 或 struct.

使用 Custom JsonConverter。根据需要修改。


[JsonConverter(typeof(ContentStructureConverter))]
public struct ContentStructure
{
    public Content ContentClass;
    public string ContentString;


    public static implicit operator ContentStructure(Content content) => new ContentStructure { ContentClass = content };
    public static implicit operator ContentStructure(string @string) => new ContentStructure { ContentString = @string };
}

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        ContentStructure contentStruct;

        if (reader.ValueType == typeof(string))
            contentStruct = reader.Value as string;
        else
            contentStruct = serializer.Deserialize<Content>(reader);

        return contentStruct;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        ContentStructure? contentStruct = value as ContentStructure?;
        if (contentStruct.HasValue && contentStruct.Value.ContentClass != null)
            serializer.Serialize(writer, contentStruct.Value.ContentClass);
        else
            serializer.Serialize(writer, contentStruct.Value.ContentString);

    }
}

另一种解决方案可能是利用 JsonSchema

首先让我们重新定义您的数据模型:

public abstract class Settings
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("type")]
    public string Type { get; set; }
}

public class SettingsV1 : Settings
{
    [JsonProperty("content")]
    public string Content { get; set; }
}

public class SettingsV2 : Settings
{
    [JsonProperty("content")]
    public Content Content { get; set; }
}

public class Content
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("duration")]
    public long Duration { get; set; }
}
  • 不用中间人 (ContentStructure) 而不是你可以有两个单独的 Settings 版本
  • 公共字段在抽象基中定义class

现在,您可以使用这两个版本化的 classes 来定义 json 模式:

private static JSchema schemaV1;
private static JSchema schemaV2;

//...
var generator = new JSchemaGenerator();
schemaV1 = generator.Generate(typeof(SettingsV1));
schemaV2 = generator.Generate(typeof(SettingsV2));

最后,您需要做的就是在使用正确的类型调用 DeserializeObject 之前进行初步检查:

Settings settings = null;
var semiParsed = JObject.Parse(json);
if (semiParsed.IsValid(schemaV1))
{
    settings = JsonConvert.DeserializeObject<SettingsV1>(json);
}
else if (semiParsed.IsValid(schemaV2))
{
    settings = JsonConvert.DeserializeObject<SettingsV2>(json);
}
else
{
    throw new NotSupportedException("The provided json format is not supported");
}