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

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

问题描述

我知道您可以使用 Newtonsoft 轻松完成此操作.然而,当我使用 .NET Core 3.0 时,我正在尝试使用新方法与 JSON 文件进行交互——即,System.Text.Json——我拒绝相信我是什么尝试做是那么困难!

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

由于我最终会在流程结束时返回另一个 JSON,因此我不想特别费心将其反序列化为模型.请注意,来自 API 的数据结构可能改变,但它总是有一个键,我可以从中与我的数据库记录进行比较.

我的代码目前是这样的:

using (WebClient wc = new WebClient()){var rawJsonDownload = wc.DownloadString("WEB API CALL");var users = JsonSerializer.Deserialize>(rawJsonDownload);foreach (var user in users.ToList()){//检查用户是否是新用户如果(检查){用户.删除(用户);}}返回 Json(用户);}

这似乎是要跳过很多的障碍,以实现对 Newtonsoft 来说相当微不足道的事情.

谁能告诉我一个更好的方法来做这件事——理想情况下,不需要 UserObject?

解决方案

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

从 .NET Core 3.0 开始,这不能用 System.Text.Json 轻松完成,因为:

  1. JsonDocumentJTokenXDocument对应的类型,为只读.它只能用于检查 JSON 值,不能用于修改或创建 JSON 值.

    目前有一个未解决的问题 可写 Json DOM #39922 跟踪这个.

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

    目前有一个未解决的问题 将 JsonPath 支持添加到 JsonDocument/JsonElement #41537 跟踪这个.

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

<预><代码>[{身份证":1,"name": "name 1",地址": {"Line1": "第 1 行",第2行":第2行"},//省略更多属性}//, 其他数组条目省略]

还有一些PredicateshouldSkip 过滤器方法指示是否不应返回具有特定 id 的条目,对应于您问题中的 CHECKS.你有哪些选择?

您可以使用 JsonDocument 并返回一些过滤后的 JsonElement 节点.如果过滤逻辑非常简单并且您不需要以任何其他方式修改 JSON,那么这是有道理的.请注意,根据 docs.因此,为了返回 JsonElement,您必须克隆它.

以下代码显示了一个示例:

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

Mockup fiddle #1 这里.

您可以创建一个部分数据模型,该模型仅反序列化过滤所需的属性,并将剩余的 JSON 绑定到 [JsonExtensionDataAttribute] 属性.strong> 这应该允许您实现必要的过滤,而无需对整个数据模型进行硬编码.

为此,请定义以下模型:

公共类 UserObject{[JsonPropertyName("id")]公共长 ID { 获取;放;}[System.Text.Json.Serialization.JsonExtensionDataAttribute]public IDictionary扩展数据 { 获取;放;}}

反序列化过滤如下:

var users = JsonSerializer.Deserialize>(rawJsonDownload);users.RemoveAll(u => shouldSkip(u.Id));返回 Json(用户);

这种方法可确保与过滤相关的属性可以适当地反序列化,而无需对 JSON 的其余部分做出任何假设.虽然这不像使用 LINQ to JSON 那样容易,但总代码复杂性受过滤检查的复杂性限制,而不是 JSON 的复杂性.事实上,我的观点是,在实践中,这种方法比纯 JsonDocument 方法更容易使用,因为如果以后需要,它可以更容易地将修改注入 JSON.>

Mockup fiddle #2 这里.

无论您选择哪个,您都可以考虑放弃 WebClient 以换用 HttpClient 并使用 async 反序列化.例如:

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

或者

var users = await JsonSerializer.DeserializeAsync>(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天全站免登陆