JSON.NET反序列化特定属性

35
我有以下的 JSON 文本:

I have the following JSON text:

{
    "PropOne": {
        "Text": "Data"
    }
    "PropTwo": "Data2"
}    
我想将 PropOne 反序列化为类型 PropOneClass,而不需要反序列化对象上的任何其他属性。 使用 JSON.NET 可以做到吗?
6个回答

46

JSON 的大小不太大,所以我会采用 Matt Johnson 的建议并将整个对象反序列化。多亏了 jcwrequests 的回答,我能够使用以下方法:

var jObject = JObject.Parse(json);
var jToken = jObject.GetValue("PropTwo");
PropTwoClass value = jToken.ToObject(typeof(PropTwoClass));

1
这也可以工作:var propTwoClass = jToken.ToObject<PropTwoClass>(); - ttugates

36
public T GetFirstInstance<T>(string propertyName, string json)
{
    using (var stringReader = new StringReader(json))
    using (var jsonReader = new JsonTextReader(stringReader))
    {
        while (jsonReader.Read())
        {
            if (jsonReader.TokenType == JsonToken.PropertyName
                && (string)jsonReader.Value == propertyName)
            {
                jsonReader.Read();

                var serializer = new JsonSerializer();
                return serializer.Deserialize<T>(jsonReader);
            }
        }
        return default(T);
    }
}

public class MyType
{
    public string Text { get; set; }
}

public void Test()
{
    string json = "{ \"PropOne\": { \"Text\": \"Data\" }, \"PropTwo\": \"Data2\" }";

    MyType myType = GetFirstInstance<MyType>("PropOne", json);

    Debug.WriteLine(myType.Text);  // "Data"
}

这种方法避免了反序列化整个对象的情况。但请注意,仅当json数据显著地大,而所需反序列化的属性位于数据早期时,才能改善性能。否则,您应该反序列化整个对象并提取您需要的部分,就像jcwrequests的答案所示。


稍微更新以提高可重用性。 - Matt Johnson-Pint
3
这个方法有缺陷,它会在树的任何层级中捕获第一个名为“PropOne”的属性,而不仅是在根目录下。可以通过跟踪层级并只接受根目录下正确的属性名称来修复它。 - hultqvist
请注意,如果您不确定要解码的JSON字符串是什么,并且需要读取属性以找出它,则以下内容也可能很有用。 - starbeamrainbowlabs

17

对于Omar的答案,一个更简单的解决方案是使用一个包装器。

class Wrapper
{
    public PropOneClass PropOne;
}

JsonConvert.Deserialize<Wrapper>(json).PropOne

我的测试发现它大约快了30%。


这是一种更加灵活的方法。创建仅具有您想要反序列化的属性的类,它将跳过所有不必要的内容并且运行速度更快。 - PaulMolloy

7
 var json = "{ "PropOne": { "Text": "Data" } "PropTwo": "Data2" }";

 JObject o = JObject.Parse(json);
 var val = o.PropTwo;

使用JSON Linq提供程序,您无需将对象反序列化为已知类型。


2

Matt的答案是目前最快的解决方案,但它有一个错误。 这是我试图修复它的方法。 该方法仅返回根级别上匹配的属性。 虽然对于有效的JSON仍存在一种天真的计数开始和结束标记的方法,但它可能会起作用。

Matt,随意将此复制到您的答案中。

public T GetFirstInstance<T>(string propertyName, string json)
{
    using (var stringReader = new StringReader(json))
    using (var jsonReader = new JsonTextReader(stringReader))
    {
        int level = 0;

        while (jsonReader.Read())
        {
            switch (jsonReader.TokenType)
            {
                case JsonToken.PropertyName:
                    if (level != 1)
                        break;
                    if ((string)jsonReader.Value == propertyName)
                    {
                        jsonReader.Read();

                        return (T)jsonReader.Value; 
                    }
                    break;

                case JsonToken.StartArray:
                case JsonToken.StartConstructor:
                case JsonToken.StartObject:
                    level++;
                    break;

                case JsonToken.EndArray:
                case JsonToken.EndConstructor:
                case JsonToken.EndObject:
                    level--;
                    break;
            }

        }
        return default(T);
    }
}

2
如何跳过令牌的子节点,以便我们只获取根级别的节点: while (jsonReader.Read()) { if ((string)jsonReader.Value == propertyName) { //返回 } jsonReader.Skip(); } - amolbk
@amolbk 是的,Skip 看起来是在 6 个月前添加的,并且可以简化代码。也许这更适合作为一个新答案。 - hultqvist

1
使用JsonIgnore - 这将导致属性被Json.Net完全忽略,无论是用于序列化还是反序列化。
此外,请查看链接

我没有包含PropOnePropTwo的复合对象,相反我有两个分别代表PropOnePropTwo的独立类。 - Omar
你也在序列化对象吗? - NomadTraveler
如果你的意思是我正在创建JSON,那么不,我得到的JSON是由第三方发送的。 - Omar
NomadTraveler,你知道是否可能只在反序列化时使用JsonIgnore吗? - Zwik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接