读取JSON的模式


Ĵ AVA小号CRIPT ö bject Ñ otion( JSON)是事实上的用于在网页API交换数据的标准。JSON是一种递归数据结构,可以可视化为键值对树。

从JSON读取信息的API有两大类:

Streaming Based: 部分解析或访问JSON。每次零件迭代时,您将零件跳过或映射到对象。部分也许{,[, field,value,:,},和] (JSON令牌!)。

1592418185396.png

_Binding based:_绑定是JSON字段到编程对象的映射;这些可能是领域的或抽象的。 JSON完全解析并映射到对象。 本文介绍了读取JSON数据的不同模式。选择Java来演示这些模式。

JSON —抽象模型绑定 JSON映射到中间提供的对象或集合对象的库。然后,迭代这些对象以与域对象进行映射或提取信息。

这些中间对象在形式上统称为“解析树”。将JSON转换为解析树的过程称为parsing²。

这些对象不能充分表达业务知识。这就是调用这些对象的原因:抽象。

In Action 考虑一下示例Google Distance Matrix API的输出中纽约和华盛顿之间的提取距离:

{
   "destination_addresses" : [ "New York, NY, USA" ],
   "origin_addresses" : [ "Washington, DC, USA" ],
   "rows" : [
      {
         "elements" : [
            {
               "distance" : {
                  "text" : "225 mi",
                  "value" : 361715
               },
               "duration" : {
                  "text" : "3 hours 49 mins",
                  "value" : 13725
               },
               "status" : "OK"
            }
         ]
      }
   ],
   "status" : "OK"
}

GSON是解析JSON的库之一。提取距离的代码如下:

JsonParser parser = new JsonParser();
// tree object structure is provided by lib.
JsonElement parsedTree = parser.parse(json);
JsonObject root = parsedTree.getAsJsonObject();
JsonObject firstRow = root.get("rows").getAsJsonArray().get(0).getAsJsonObject();
JsonObject firstElement = firstRow.get("elements").getAsJsonArray().get(0).getAsJsonObject();

// finally, distance is extracted ! Phew 
String distance = firstElement.get("distance").getAsJsonObject().get("value").getAsString();

Repl.it→ https: //repl.it/@DM8tyProgrammer/NonDomainBinding

JSON —域模型绑定 几乎整个JSON都直接映射到等效的Domain或Business Objects;域对象模仿具有相同字段和兼容类型的JSON结构。

In Action 考虑来自Web API的Twitter的Tweet对象,以演示此模式。

{
 "created_at": "Wed Oct 10 20:19:24 +0000 2018",
 "id": 1050118621198921728,
 "id_str": "1050118621198921728",
 "text": "To make room for more expression, we will now count all emojis as equal—including those with gender and skin t… https://t.co/MkGjXf9aXm",
 "user": {...}
.
.
.
}

等效类适用于上述JSON:

class Tweet {

  private long id;
  private String id_str;
  private String text;
  private String source;
  private User user;
  private String created_at;

}
{ "user": {
    "id": 6253282,
    "id_str": "6253282",
    "name": "Twitter API",
    "screen_name": "TwitterAPI",
    "location": "San Francisco, CA",
    "url": "https://developer.twitter.com",
    "description": "The Real Twitter API. Tweets about API changes, service issues and our Developer Platform. Don't get an answer? It's on my website."
.

 }

}
class User {

  private long id;
  private String id_str;
  private String name;
  private String description;
  private String screen_name;
  private String url;
  private String location;

}

您可以验证:类具有相同名称和兼容类型的逐字段镜像JSON。一些库在映射方面提供了放宽:缺少的字段,未知的字段,字段名称不匹配,类型不匹配和转换。

Jackson是著名的领域对象映射库。Jackson提供了一个ObjectMapper API来读取或写入JSON。映射代码为:

ObjectMapper objectMapper = new ObjectMapper();
Tweet tweet = objectMapper.readValue(tweetAsJson, Tweet.class);

// process Tweet object

Repl.it→ https: //repl.it/@DM8tyProgrammer/jackson 基于表达式的数据提取 可以使用JSONPath提取JSON中的选定数据,JSONPath 是用于指向字段的代数表达式。XPath启发了JSONPath的想法。

使用JSONPath的提取可以描述为:

  1. JSON Parsing::JSON被解析为抽象对象或解析树。
  2. Expression Evaluation:表达式被传递到上一步公开的API以查询JSON。

JSONPath表达式 在JSONPath上下文中,JSON被视为Tree。将以下元素串联起来以表达表达式:

  • JSON的根由选择$
  • .选择直接子字段。
  • .. 选择任何级别的子字段。
  • ['<field name>']或只是命名以选择字段。
  • [<number>]访问第n个数组元素。
  • [*] 定位到数组的所有字段。

In Action 再次,从示例Google Distance Matrix API的输出中考虑纽约和华盛顿之间的提取距离:

{
   "destination_addresses" : [ "New York, NY, USA" ],
   "origin_addresses" : [ "Washington, DC, USA" ],
   "rows" : [
      {
         "elements" : [
            {
               "distance" : {
                  "text" : "225 mi",
                  "value" : 361715
               },
               "duration" : {
                  "text" : "3 hours 49 mins",
                  "value" : 13725
               },
               "status" : "OK"
            }
         ]
      }
   ],
   "status" : "OK"
}

The JSON path would be: $.rows[0].elements[0].distance.value. JSONPath can be evaluated with Jayway’s Jsonpath (notice name!) library. The code snippet for the extracting distance is as:

ReadContext ctx = JsonPath.parse(distanceJson);
Double distance = ctx.read("$.rows[0].elements[0].distance.value", Double.class);
Repl.it  https://repl.it/@DM8tyProgrammer/jsonpath

JSON Streaming Parsing 前面提到,流JSON意味着部分地迭代JSON。这些部分正式称为令牌。这些可以是单个字符或字符组。 In JSON Format Specification, the following tokens are defined:

  • Object Sentinels:: {:Start of Object, }: End of Object
  • Array Sentinels:: [: Start of Array, ]: End of Array
  • Literal Types:: Number, String, Boolean (true, false), null.

In Action 考虑一个简单的JSON来解析:

{
  "name": "Mighty",
  "handler": "DM8typrogrammer"
}

GSON和Jackson均具有Streaming API。杰克逊被任意选择进行示威。下面展示了通过流令牌解析JSON的代码;每次迭代时,数据将按以下方式推送到集合:

String json = "{\"name\": \"mighty\", \"handler\": \"DM8typrogrammer\"}";
JsonFactory jsonfactory = new JsonFactory();
JsonParser jsonParser = jsonfactory.createParser(json);

// putting values in map
Map<String, String> data = new HashMap<>();

JsonToken currentToken = jsonParser.nextToken();
while (currentToken != JsonToken.END_OBJECT) {

  if (currentToken == JsonToken.FIELD_NAME) {
    String fieldname = jsonParser.getCurrentName();
    jsonParser.nextToken(); // move to value

    // pushing data to map
    data.put(fieldname, jsonParser.getText());    
  }

  // iterating token by token
  currentToken = jsonParser.nextToken();
}
jsonParser.close();
System.out.println(data);

Repl.it→ https: //repl.it/@DM8tyProgrammer/stream-json

可以将代码理解为遍历具有JSON tokens:

1592633416045.png

结束语 Binding API在内部流式传输JSON,以将其转换为对象形式。可以说Binding是高级API。

流,抽象模型绑定和基于表达式的提取用于 从JSON选择数据的一部分。域模型绑定方法用于提取完整的JSON信息。

基于表达式的JSON提取是Abstract Model Binding方法的声明性变体 。您无需手动编写代码来解析树,而是将规范传递给API以从JSON提取数据。这是一种简洁灵活的方法;但是,它是一个较慢的变体。


原文链接:http://codingdict.com