使用System.Text.Json修改JSON文件 [英] Modifying a JSON file using System.Text.Json

查看:221
本文介绍了使用System.Text.Json修改JSON文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道您可以使用Newtonsoft轻松地做到这一点.但是,当我使用.NET Core 3.0时,我正在尝试使用与JSON文件进行交互的新方法(即System.Text.Json),但我拒绝相信我要做的所有事情都很困难!/p>

我的应用程序需要列出尚未添加到我的数据库中的用户.为了获取所有用户的完整列表,该应用程序从Web API检索JSON字符串.现在,我需要循环浏览每个用户,并检查是否已将它们添加到我的应用程序中,然后再将新的JSON列表返回到我的视图,以便它可以向最终用户显示新的潜在用户.

由于我最终将在过程结束时返回另一个JSON,所以我特别不想打扰将其反序列化为模型.请注意,API 中的数据结构可能会发生变化,但它总是会具有一个键,可以与我的数据库记录进行比较.

我的代码当前如下所示:

 using (WebClient wc = new WebClient())
{
    var rawJsonDownload = wc.DownloadString("WEB API CALL");
    var users =  JsonSerializer.Deserialize<List<UserObject>>(rawJsonDownload);

    foreach (var user in users.ToList())
    {
        //Check if User is new
        if (CHECKS)
        {
            users.Remove(user);
        }
    }

    return Json(users); 
}
 

为了获得与Newtonsoft相比相当微不足道的东西,这似乎是一大堆 .

有人能建议我做一个更好的方法吗?理想情况下,不需要UserObject吗?

解决方案

您的问题是您希望检索,过滤和传递一些JSON,而无需为该JSON定义完整的数据模型.使用Json.NET,您可以为此目的使用 LINQ to JSON .您的问题是,当前可以使用System.Text.Json轻松解决吗?

从.NET Core 3.0开始,使用System.Text.Json很难做到这一点,因为:

  1. <与JTokenXDocument相对应的类型c5> 是只读的.它只能用于检查 JSON值,而不能用于修改或创建JSON值.

    当前存在一个未解决的问题 可写Json DOM#39922 对此进行跟踪.

  2. System.Text.Json不支持 JSONPath ,在此类应用程序中通常非常方便

    当前存在一个未解决的问题 将JsonPath支持添加到JsonDocument/JsonElement#41537 对此进行跟踪.

话虽如此,假设您具有以下JSON:

[
  {
    "id": 1,
    "name": "name 1",
    "address": {
      "Line1": "line 1",
      "Line2": "line 2"
    },
    // More properties omitted
  }
  //, Other array entries omitted
]

和某些Predicate<long> shouldSkip过滤器方法,该方法指示是否应返回与特定问题CHECKS相对应的具有特定id的条目.您有什么选择?

您可以使用JsonDocument并返回文档.因此,为了返回JsonElement,您必须

模型小提琴#1 此处.

您可以创建一个 partial 数据模型,该数据模型仅反序列化要过滤的属性,其余JSON绑定到

反序列化和过滤如下:

var users = JsonSerializer.Deserialize<List<UserObject>>(rawJsonDownload);
users.RemoveAll(u => shouldSkip(u.Id));

return Json(users);

这种方法确保了与过滤相关的属性可以适当地反序列化,而无需对JSON的其余部分做任何假设.尽管这并不像使用LINQ to JSON那样容易,但是总代码复杂度受筛选检查的复杂度(而不是JSON的复杂度)的限制.实际上,我的观点是,实际上,这种方法比纯JsonDocument方法更容易使用,因为如果以后需要,它可以更轻松地注入对JSON的修改.

样机小提琴#2 此处.

无论选择哪种方式,您都可以考虑放弃WebClient来使用

var users = await JsonSerializer.DeserializeAsync<List<UserObject>>(await httpClient.GetStreamAsync("WEB API CALL"));

您还需要转换您的API方法也必须为async.

I know you can do this easily with Newtonsoft. As I am working with .NET Core 3.0, however, I am trying to use the new methods for interacting with JSON files —i.e., System.Text.Json—and I refuse to believe that what I am trying to do is all that difficult!

My application needs to list users that have not already been added to my database. In order to get the full list of all users, the app retrieves a JSON string from a web API. I now need to cycle through each of these users and check if they have already been added to my application before returning a new JSON list to my view so that it can display the new potential users to the end user.

As I am ultimately returning another JSON at the end of the process, I don't especially want to bother deserializing it to a model. Note that the structure of data from the API could change, but it will always have a key from which I can compare to my database records.

My code currently looks like this:

using (WebClient wc = new WebClient())
{
    var rawJsonDownload = wc.DownloadString("WEB API CALL");
    var users =  JsonSerializer.Deserialize<List<UserObject>>(rawJsonDownload);

    foreach (var user in users.ToList())
    {
        //Check if User is new
        if (CHECKS)
        {
            users.Remove(user);
        }
    }

    return Json(users); 
}

This seems like a lot of hoops to jump through in order to achieve something that would be fairly trivial with Newtonsoft.

Can anyone advise me on a better way of doing this—and, ideally, without the need for the UserObject?

解决方案

Your problem is that you would like to retrieve, filter, and pass along some JSON without needing to define a complete data model for that JSON. With Json.NET, you could use LINQ to JSON for this purpose. Your question is, can this currently be solved as easily with System.Text.Json?

As of .NET Core 3.0, this cannot be done quite as easily with System.Text.Json because:

  1. JsonDocument, the type corresponding to JToken or XDocument, is read-only. It can be used only to examine JSON values, not to modify or create JSON values.

    There is currently an open issue Writable Json DOM #39922 tracking this.

  2. System.Text.Json has no support for JSONPath which is often quite convenient in such applications.

    There is currently an open issue Add JsonPath support to JsonDocument/JsonElement #41537 tracking this.

That being said, imagine you have the following JSON:

[
  {
    "id": 1,
    "name": "name 1",
    "address": {
      "Line1": "line 1",
      "Line2": "line 2"
    },
    // More properties omitted
  }
  //, Other array entries omitted
]

And some Predicate<long> shouldSkip filter method indicating whether an entry with a specific id should not be returned, corresponding to CHECKS in your question. What are your options?

You could use JsonDocument and return some filtered set of JsonElement nodes. This makes sense if the filtering logic is very simple and you don't need to modify the JSON in any other way. Be aware that JsonDocument is disposable, and in fact must needs be disposed to minimize the impact of the garbage collector (GC) in high-usage scenarios, according to the docs. Thus, in order to return a JsonElement you must clone it.

The following code shows an example of this:

using var usersDocument = JsonDocument.Parse(rawJsonDownload);
var users = usersDocument.RootElement.EnumerateArray()
    .Where(e => !shouldSkip(e.GetProperty("id").GetInt64()))
    .Select(e => e.Clone())
    .ToList();

return Json(users);

Mockup fiddle #1 here.

You could create a partial data model that deserializes only the properties you need for filtering, with the remaining JSON bound to a [JsonExtensionDataAttribute] property. This should allow you to implement the necessary filtering without needing to hardcode an entire data model.

To do this, define the following model:

public class UserObject
{
    [JsonPropertyName("id")]
    public long Id { get; set; }

    [System.Text.Json.Serialization.JsonExtensionDataAttribute]
    public IDictionary<string, object> ExtensionData { get; set; }
}

And deserialize and filter as follows:

var users = JsonSerializer.Deserialize<List<UserObject>>(rawJsonDownload);
users.RemoveAll(u => shouldSkip(u.Id));

return Json(users);

This approach ensures that properties relevant to filtering can be deserialized appropriately without needing to make any assumptions about the remainder of the JSON. While this isn't as quite easy as using LINQ to JSON, the total code complexity is bounded by the complexity of the filtering checks, not the complexity of the JSON. And in fact my opinion is that this approach is, in practice, a little easier to work with than the pure JsonDocument approach because it makes it somewhat easier to inject modifications to the JSON if required later.

Mockup fiddle #2 here.

No matter which you choose, you might consider ditching WebClient for HttpClient and using async deserialization. E.g.:

var httpClient = new HttpClient();
using var usersDocument = await JsonDocument.ParseAsync(await httpClient.GetStreamAsync("WEB API CALL"));

Or

var users = await JsonSerializer.DeserializeAsync<List<UserObject>>(await httpClient.GetStreamAsync("WEB API CALL"));

You would need to convert your API method to be async as well.

这篇关于使用System.Text.Json修改JSON文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆