如何通过 Gson 获取过滤的 JSON 节点?

Pav*_*ari 5 json gson

我正在使用 Gson 来解析和写入 JSON 数据。

[{
    "Source":"IN",
    "Type":"PRD",
    "Name":"ABC"
    "IsActive":true
}, {
    "Source":"IN",
    "Type":"COB",
    "Name":"XYZ"
    "IsActive":true
}, {
    "Source":"PRo",
    "Type":"RPT",
    "Name":"TEST"
    "IsActive":true
}]
Run Code Online (Sandbox Code Playgroud)

如何根据某些属性值获取 JSON 节点?例如,我想获得一个值为Source= PRoType=RPTName=的节点TEST

我怎样才能用 Gson 做到这一点?

Lyu*_*riv 13

正如我上面所说,Gson 不是一个查询库。但是这里至少有四种策略可以与 Gson 一起使用:

  • 使用内存策略
    • 使用 JSON 树模型
    • 使用动态映射
    • 使用静态映射
  • 使用流媒体策略

以上每一种都有其自身的优点和缺点。对特定节点应用一些修改是一个微不足道的 filter-and-then-forEach 问题,并且可以通过命令式for/if或使用 Java 8 Stream API轻松解决。

JSON 树模型

JSON 树模型用于在内存中构建特定 JSON 文档的 JSON 表示。拥有类似 DOM 的结构,您可以轻松地遍历和修改任何树叶。但是,它需要大量内存。

try ( final Reader reader = getPackageResourceReader(Q43200432.class, "q43200432.json") ) {
    final JsonArray rows = jsonParser.parse(reader).getAsJsonArray();
    StreamSupport.stream(rows.spliterator(), false)
            .map(JsonElement::getAsJsonObject)
            .filter(jsonObject -> jsonObject.get("Source").getAsString().equals("PRo")
                    && jsonObject.get("Type").getAsString().equals("RPT")
                    && jsonObject.get("Name").getAsString().equals("TEST")
            )
            .forEach(jsonObject -> jsonObject.add("IsActive", new JsonPrimitive(false)));
    System.out.println(rows);
}
Run Code Online (Sandbox Code Playgroud)

JSON 映射到 Map<String, Object>

与上面类似,但行结构甚至不与 Gson JSON 树模型 API 绑定,因此值可以是字面上的任何类型。

private static final Gson gson = new Gson();

private static final Type rowListType = new TypeToken<List<Map<String, Object>>>() {
}.getType();

...

try ( final Reader reader = getPackageResourceReader(Q43200432.class, "q43200432.json") ) {
    final List<Map<String, Object>> rows = gson.fromJson(reader, rowListType);
    rows.stream()
            .filter(r -> r.get("Source").equals("PRo")
                    && r.get("Type").equals("RPT")
                    && r.get("Name").equals("TEST"))
            .forEach(r -> r.put("IsActive", false));
    System.out.println(gson.toJson(rows, rowListType));
}
Run Code Online (Sandbox Code Playgroud)

JSON 映射到自定义映射(普通的旧 Java 对象)

final class Row {

    @SerializedName("Source")
    String source;

    @SerializedName("Type")
    String type;

    @SerializedName("Name")
    String name;

    @SerializedName("IsActive")
    boolean isActive;

}
Run Code Online (Sandbox Code Playgroud)
private static final Gson gson = new Gson();

private static final Type rowListType = new TypeToken<List<Row>>() {
}.getType();

...

try ( final Reader reader = getPackageResourceReader(Q43200432.class, "q43200432.json") ) {
    final List<Row> rows = gson.fromJson(reader, rowListType);
    rows.stream()
            .filter(r -> "PRo".equals(r.source) && "RPT".equals(r.type) && "TEST".equals(r.name))
            .forEach(r -> r.isActive = false);
    System.out.println(gson.toJson(rows, rowListType));
}
Run Code Online (Sandbox Code Playgroud)

流媒体

流允许通过令牌读取 JSON 令牌并进行某种分析。由于流不需要将整个文档存储在内存中,因此它甚至可以处理无限流,但是它看起来可能有点难以使用。

private static final Gson gson = new Gson();

...

final TypeAdapter<JsonObject> jsonObjectTypeAdapter = gson.getAdapter(JsonObject.class);
final Writer writer = new StringWriter();
try ( final JsonReader jsonReader = new JsonReader(getPackageResourceReader(Q43200432.class, "q43200432.json"));
      final JsonWriter jsonWriter = new JsonWriter(writer) ) {
    jsonReader.beginArray();
    jsonWriter.beginArray();
    while ( jsonReader.hasNext() ) {
        final JsonObject jsonObject = jsonObjectTypeAdapter.read(jsonReader);
        if ( jsonObject.get("Source").getAsString().equals("PRo")
                && jsonObject.get("Type").getAsString().equals("RPT")
                && jsonObject.get("Name").getAsString().equals("TEST") ) {
            jsonObject.add("IsActive", new JsonPrimitive(false));
        }
        jsonObjectTypeAdapter.write(jsonWriter, jsonObject);
    }
    jsonReader.endArray();
    jsonWriter.endArray();
}
System.out.println(writer);
Run Code Online (Sandbox Code Playgroud)

以上四种策略都会产生以下输出:

[{"Source":"IN","Type":"PRD","Name":"ABC","IsActive":true},{"Source":"IN","Type":"COB", "Name":"XYZ","IsActive":true},{"Source":"PRo","Type":"RPT","Name":"TEST","IsActive":false}]

请注意,位于 的最后一个元素$[2].IsActive设置为false