c#反序列化json片段
小伙伴们大家好,在日常工作中,难免与json数据打交道,通常在处理大型json文档时,如果只对一小部分数据感兴趣,这时可能会比较头疼,往往为了解析一个片段信息而不得不定义整个json结构的c#类。
今天我为大家分享一下我日常工作中如解析取片段信息,为了使用和拓展,这里分享使用 http:// Json.NET(newtonsoft) 以及微软原生(System.Text.Json)两种方法供大家参考。事不宜迟,现在开始吧!~
准备工作
为了方便起见,这里提供一段较为简短的json数据:
{
"title": "zhihu666",
"publisher": "zhihu",
"shortDescription": "这是简短描述",
"description": "这是详细描述",
"contact": {
"companyName": "知乎",
"serviceProviderName": "张三",
"serviceEmail": "123@zhihu.com",
"servicePhone": "17717410000",
"serviceQQ": "",
"serviceWeChat": "",
"weibo": ""
}
假设我们我们在这数据中只关心contact中的内容,下面我们定义一下contact的实体,下面我们需要将json中的contact的片段数据解析到Contact实体中。
public class Contact
public string CompanyName { get; set; }
public string ServiceProviderName { get; set; }
public string ServiceEmail { get; set; }
public string ServicePhone { get; set; }
public string ServiceWorkTime { get; set; }
public string ServiceQQ { get; set; }
public string ServiceWeChat { get; set; }
public string Weibo { get; set; }
使用 http:// json.net 解析
使用 http:// json.net 我们可以借助SelectToken获取片段或者节点的值,SelectToken支持三种方式获取:
- 属性名或数组索引获取(以点分隔)
- 使用JSONPath
- 使用LINQ
这里我们使用的是第一种的方式,剩下两种大家可以去 阅读文档 进行拓展。
通过SelectToken返回的是JToken类型,如果在指定的路径未搜寻到,则会 返回null。
基于上面的知识,我们想获取片段的实体,就可以分解为以下的步骤:
- 将json转换为JObject
- 使用SelectToken获取感兴趣的信息,得到jtoken
- 使用jtoken的ToObject方法转成对应的实体,或者直接将jtoken转换成对应的类型
比如我想获取Contact的实体信息:
var jo = JObject.Parse(json);
var jToken = jo.SelectToken("contact");
if (jToken != null)
var contact = jToken.ToObject<Contact>();
}
如果您不想获取某个实体信息,只对 某个属性的值 感兴趣,比如只想获取companyName的信息,那么直接将jtoken转换成对应的值即可,示例如下
var jo = JObject.Parse(json);
var companyName = (string)jo.SelectToken("contact.companyName");
当然这只是最简单的情况,在日常工作中,您可能需要获取多个属性或者片段的值,甚至需要多属性多片段的值,如果继续采取上述方法,就不得不获取每一个您所关心的值,最后再拼接在一起,显然这么做并不是一个好主意。这时候我们就需要手动实现一个 JsonConverter。
先来一段json数据
{
"node1": {
"node1node1": "node1node1value",
"node1node2": [ "value1", "value2" ],
"node1node3": {
"node1node3node1": "node1node3node1value"
"node2": true,
"node3": {
"node3node1": "node3SubNode1Value",
"node3node2": {
"node3node2node1": {
"node3node2node1node1": [ 1, 2, 3 ]
"node3node2node2": "node3node2node1value"
}
对于上面的json数据,假如我比较关心node1node2,node2,以及node3node2node1node1节点的信息。下面我们来创建一个类用来反序列化这些信息。
[JsonConverter(typeof(JsonPathConverter))]
public class JsonModel
[JsonProperty("node1.node1node2")]
public IList<string> Node1Array { get; set; }
[JsonProperty("node2")]
public bool Node2 { get; set; }
[JsonProperty("node3.node3node2.node3node2node1.node3node2node1node1")]
public IList<int> Node3Array { get; set; }
这些JsonPropert中的参数就是这个属性对应的json中的路径,而这个JsonPathConverter是我们接下来要实现的内容,示例代码如下
public class JsonPathConverter : JsonConverter
public override bool CanWrite => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var jObject = JObject.Load(reader);
var targetObj = Activator.CreateInstance(objectType);
foreach (var prop in objectType.GetProperties().Where(p => p.CanRead && p.CanWrite))
var jsonPropertyAttr = prop.GetCustomAttributes(true).OfType<JsonPropertyAttribute>().FirstOrDefault();
if (jsonPropertyAttr == null)
throw new JsonReaderException($"{nameof(JsonPropertyAttribute)} is mandatory when using {nameof(JsonPathConverter)}");
var jsonPath = jsonPropertyAttr.PropertyName;
var token = jObject.SelectToken(jsonPath);
if (token != null && token.Type != JTokenType.Null)
var jsonConverterAttr = prop.GetCustomAttributes(true).OfType<JsonConverterAttribute>().FirstOrDefault();
object value;
if (jsonConverterAttr == null)
serializer.Converters.Clear();
value = token.ToObject(prop.PropertyType, serializer);
value = JsonConvert.DeserializeObject(token.ToString(), prop.PropertyType,
(JsonConverter)Activator.CreateInstance(jsonConverterAttr.ConverterType));
prop.SetValue(targetObj, value, null);
return targetObj;
public override bool CanConvert(Type objectType)
return true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
}
当这些都定义好后,反序列化就跟平时的操作一样了
var result = JsonConvert.DeserializeObject<JsonModel>(json);
怎么样是不是非常的简单。这两种方法都比较常用,如何取舍还应看获取内容多少,获取少量数据的话,就不需要搞得那么复杂。
使用System.Text.Json解析
好多小伙伴可能早就知道,在.NET Core 3.0中微软加入了对JSON的内置支持。如果不愿意引用第三方的json库,可以试试使用微软原生的解决方案。原生的json api拥有更好的性能,但是功能远不如 http:// json.net 丰富。下面我们尝试使用原生的json api来解析contact片段信息。
大致的步骤如下:
- 将json转换为JsonDocument
- 通过RootElement获取到document的根元素
- 通过GetProperty方法,一级一级的找到您关心的数据的位置
示例代码如下
using var document = JsonDocument.Parse(json);
var contactEle = document.RootElement.GetProperty("contact");
到这里我们就获取到contact的JsonElement,值得主意的是,和 http:// json.net 不同的是,如果找不到该属性,则会抛出KeyNotFoundException。当然微软也提供了Try***方法进行读取。比如
if(document.RootElement.TryGetProperty("contact", out var paraPosition))
//do something
另外值得注意的是,相比于 http:// json.net ,原生的api并没有提供ToObject扩展方法。但是在.NET 6官方提供了Deserialize方法,用法如下
var myClass = jDoc.RootElement.GetProperty("contact").Deserialize<Contact>();
如果是.NET6之前的版本,可以自己定义一个ToObject方法,也很方便
public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)