【问题标题】:Can I parse json either into a string or another concrete type as object?我可以将 json 解析为字符串或其他具体类型作为对象吗?
【发布时间】:2015-10-12 12:12:46
【问题描述】:

我想要object 类型的属性,它可以是stringTemplate 类型。

是否可以告诉 Json.NET 将某些内容解析为几种指定类型之一?

class MyClass
{
    public object Template { get; set; }
}

在哪里Template = "TemplateName"

{
    "Template": "TemplateName"
}

Template = new Template()

{
    "Template": { "Name": "OtherTamplate", ... }
}

更新:

我尝试遵循@krillgar 的建议并创建自定义JsonConverter,但不幸的是CanConvert 方法仅接收目标类型,在本例中为object。这些信息不足以告诉它是否可以反序列化(如果我有其他 object 属性)。我想我毕竟需要它是 Template 或创建一个派生类型,如 TemplateReference 或其他东西:

class myconverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType)
    {
        // objectType = typeof(object)
        throw new NotImplementedException();
    }
}

Configuration = JsonConvert.DeserializeObject<MyClass>(text, new myconverter());

免责声明

这个问题曾经作为How to deserialize a JSON property that can be two different data types using Json.NET 的副本被关闭。因为在写我的问题时我还不知道已经有一个类似的问题我想澄清它们之间的区别以防止它在将来被关闭:

另一个问题是关于如何将不同的值反序列化为 concrete 类型,而我的问题是将不同的值反序列化为 object。乍一看似乎是一样的,因为在这两个示例中,只有属性的类型不同,但它对整个应用程序设计有巨大的影响。对我来说重要的是,我可以使用 object 来存储不同的专用类型,而不是一种具有多种职责的类型。

【问题讨论】:

  • 当然。你必须为它创建一个custom deserializer
  • @krillgar:谢谢你的线索 :) 我没想到我会需要它复杂。我曾希望有一些 secret 属性来添加 to 属性并指定可能的类型或反序列化器的设置。
  • 您需要为 MyClass 而不是 Template 定制 desalizer。然后 MyClass 解串器必须决定模板属性是字符串还是模板,并调用适当的标准解串器。
  • 太好了,感谢您将其作为副本关闭。链接的问题不会以任何方式回答我的问题。我想将多种类型反序列化为对象而不是特定类型。
  • @Ben 我喜欢这个想法,但是我不确定我是否真的需要自己解析每个属性。如果我为MyClass 创建一个转换器并覆盖ReadJson 方法,我可以只处理我想以不同方式处理的类型或属性,并为其他所有内容使用一些默认转换器吗?

标签: c# json json.net


【解决方案1】:

这个问题可以通过使用自定义JsonConverter 来解决。这是适用于这种情况的通用版本:

class ObjectOrStringConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // CanConvert is not called when the [JsonConverter] attribute is used
        return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Object)
        {
            return token.ToObject<T>(serializer);
        }
        return token.ToString();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

要使用转换器,您只需将[JsonConverter] 属性添加到您的类中的属性,该属性可以是字符串或对象。泛型类型参数必须与您期望的非字符串对象的类型相匹配。

class MyClass
{
    [JsonConverter(typeof(ObjectOrStringConverter<Template>))]
    public object Template { get; set; }
}

下面是转换器的实际演示:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("--- first run ---");

        string json = @"
        {
            ""Template"": ""TemplateName""
        }";

        DeserializeAndDump(json);

        Console.WriteLine("--- second run ---");

        json = @"
        {
            ""Template"": { ""Name"": ""OtherTemplate"" }
        }";

        DeserializeAndDump(json);
    }

    static void DeserializeAndDump(string json)
    {
        MyClass obj = JsonConvert.DeserializeObject<MyClass>(json);
        if (obj.Template == null)
        {
            Console.WriteLine("Template property is null");
        }
        else
        {
            Console.WriteLine("Template property is a " + obj.Template.GetType().Name);

            string name = "(unknown)";
            if (obj.Template is Template) name = ((Template)obj.Template).Name;
            else if (obj.Template is string) name = (string)obj.Template;

            Console.WriteLine("Template name is \"" + name + "\"");
        }
        Console.WriteLine();
    }
}

class Template
{
    public string Name { get; set; }
}

这是上面的输出:

--- first run ---
Template property is a String
Template name is "TemplateName"

--- second run ---
Template property is a Template
Template name is "OtherTemplate"

小提琴:https://dotnetfiddle.net/Lw3RaN

【讨论】:

  • 谢谢 :) 我现在明白我做错了什么。在我的实验中,我指定了属性并将转换器传递给DeserializeObject 方法。我不知道我只能使用该属性,这样CanConvert 方法就不会被调用。这使整个任务变得更加简单:] 稍后我会检查它。
  • 啊。是的,如果你以前没有做过转换器,那可能有点“陷阱”。
【解决方案2】:

我不知道你是否可以这样做,但你可以走另一条路。将您的“模板”属性更改为Template 而不是object,并使用Template 类的自定义属性来了解您是否要将其序列化为Templatestring

class MyClass
{
    [JsonConverter(typeof(TemplateConverter))]
    public Template Template { get; set; }
}

class Template
{
    /* Your Template class */

    public Type TypeToSerializeInto { get; private set; }
}

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
        Template val = value as Template;

        writer.WriteStartObject();
        writer.WritePropertyName("Template");

        if (val.TypeToSerializeInto == typeof(Template))
            serializer.Serialize(writer, val);
        else if (val.TypeToSerializeInto == typeof(string))
            serializer.Serialize(writer, val.Name);

        writer.WriteEndObject();
    }
}

【讨论】:

  • 我现在就走这条路,因为这是我目前唯一能做的事情,但问题仍然没有得到解答。
  • @t3chb0t 这很公平。
【解决方案3】:

如果您有一个属性,该类型是抽象类型(如对象),在反序列化时,您可以通过在某处序列化该特定类型的名称来知道被序列化的特定类型。 所以你的 json 应该是这样的:

{ “我的课”: { "模板": "某个名字", “类型”:“系统.字符串” } }

这种反序列化方式可以在序列化之前检查该属性的类型(在本例中为字符串)

确定类型的另一种方法是检查 json 结构,如下所示: C#: Deserializing JSON when one field can be different types

【讨论】:

  • 我认为将实际类型放在那里不是一个好主意,但无论如何感谢您的建议:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-12
  • 1970-01-01
  • 2014-03-17
  • 2013-03-12
  • 1970-01-01
  • 1970-01-01
  • 2012-01-30
相关资源
最近更新 更多