使用 Jackson 从请求 JSON 中提取某些字段和值

jus*_*ser 2 json jackson spring-boot

我正在寻找一种方法,仅包含接收到我的服务的 JSON 请求中的某些字段,然后相应地记录它们,以避免记录太多数据。

要包含的字段和日志将在属性文件中配置。

由于我的服务使用 Spring Boot,Jackson JAR 应该已经可用。

由于请求 JSON 很复杂,包含嵌套数组和对象作为字段,因此我不确定我是否可以通过 Jackson 实现我的要求。

有没有办法使用 Jackson API 从输入请求 JSON 中仅提取某些字段及其值?

基本上,我正在研究两个用例。

1.从Json String中选择一个或多个元素(我打算通过配置传递),然后渲染它们

2.选择并更新一个或多个内联元素。我想在渲染元素之前屏蔽它们的值。

我提供了用于选择我使用的元素和 Json 的代码以及我期望的代码,如下所示。

public String getValues(String key,String jsonString){
    String fieldNodeStr =null;
    try {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node = mapper.readTree(jsonString);
        JsonNode fieldNode = node.at(key);
        fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
        System.out.println(fieldNodeStr);
    } catch (IOException e) {
        System.out.println("IOException:",e);
    }catch (Exception e) {
        System.out.println("Exception:",e);
    }
}   
Run Code Online (Sandbox Code Playgroud)

我的 Json 如下,

{
    "employeeId" : "5353",
    "salesId" : "sales005",
    "manager" : {
        "userId" : "managerUser",
        "isSuperUser" : false,
        "firstName":"manager first name",
        "lastName":"manager last name"
            },
    "administrator" : {
        "userId" : "administratorUser",
        "isSuperUser" : false,
        "firstName":"admin first name",
        "lastName":"admin last name"
    },
    "requester" : [
                    {
                            "id":"1234",
                            "demographic" : {
                                                "firstName" : "hello",
                                                "lastName" : "hai"
                                             }
                     },
                     {
                            "id":"1235",
                            "demographic" : {
                                                "firstName" : "welcome",
                                                "lastName" : "user"
                                            }
                      }
                   ]

}
Run Code Online (Sandbox Code Playgroud)

我有以下两个问题。

如果我传递“/manager/userId”、“/administrator/isSuperUser”(或)“/salesId”,我就能获得预期值。

但是,如果想获取 /requester/id (OR) /requester/demographic (OR) /requester/demographic/lastName ,我无法获取。我得到空值。

当我分别通过“/ requester / id”(或)“/ requester / demographic”时,我预计会出现以下情况。

"requester" : [
                    {
                            "id":"1234"
                     },
                     {
                            "id":"1235"
                      }
                ]


 "requester" : [
                    {
                            "demographic" : {
                                                "firstName" : "hello",
                                                "lastName" : "hai"
                                             }
                     },
                     {
                            "demographic" : {
                                                "firstName" : "welcome",
                                                "lastName" : "user"
                                            }
                      }
                   ]
Run Code Online (Sandbox Code Playgroud)

除了 fetch 之外,我还想在使用 JsonPointer 定位它们后内联更新值

我的更新代码如下,

public String findAndUpdate(String key,String jsonString,String repValue){
    String fieldNodeStr =null;
    try {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node = mapper.readTree(jsonString);
        JsonNode fieldNode = node.at(key);

        //Update the value of located node to a different one
        ((ObjectNode) fieldNode).put(key,repValue);

        fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
        System.out.println(fieldNodeStr);
    } catch (IOException e) {
        System.out.println("IOException:",e);
    }catch (Exception e) {
        System.out.println("Exception:",e);
    }
    return fieldNodeStr;
}
Run Code Online (Sandbox Code Playgroud)

当我传递“/manager/userId”作为键值时,出现以下错误,

17:21:24.829 [main] ERROR com.demo.jsondemo.TestClass - Exception:
java.lang.ClassCastException: com.fasterxml.jackson.databind.node.TextNode cannot be cast to com.fasterxml.jackson.databind.node.ObjectNode
    at com.demo.jsondemo.TestClass.findAndUpdate(TestClass.java:95) [classes/:na]
    at com.demo.jsondemo.TestClass.main(TestClass.java:221) [classes/:na]
Run Code Online (Sandbox Code Playgroud)

cas*_*lin 8

JSON 指针

您可以使用RFC 6901定义的 JSON 指针(一种用于标识 JSON 文档中特定值的字符串语法)。

出于示例目的,请考虑以下 JSON:

{
  "firstName": "John",
  "lastName": "Doe",
  "address": {
    "street": "21 2nd Street",
    "city": "New York",
    "postalCode": "10021-3100",
    "coordinates": {
      "latitude": 40.7250387,
      "longitude": -73.9932568
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

要查询coordinates节点,您可以使用以下 JSON 指针表达式:

/address/coordinates
Run Code Online (Sandbox Code Playgroud)

JSON 指针和 Jackson

Jackson 2.3.0引入了对JSON Pointer的支持,它可以如下使用:

String json = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"address\":{\"street\":"
        + "\"21 2nd Street\",\"city\":\"New York\",\"postalCode\":\"10021-3100\","
        + "\"coordinates\":{\"latitude\":40.7250387,\"longitude\":-73.9932568}}}";

ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
JsonNode coordinatesNode = node.at("/address/coordinates");
Run Code Online (Sandbox Code Playgroud)

coordinates节点可以被解析为一个 bean:

Coordinates coordinates = mapper.treeToValue(coordinatesNode, Coordinates.class);
Run Code Online (Sandbox Code Playgroud)

或者可以写成String

String coordinatesNodeAsString = mapper.writerWithDefaultPrettyPrinter()
                                       .writeValueAsString(coordinatesNode);
Run Code Online (Sandbox Code Playgroud)

Jayway JsonPath

JSON 指针的一个很好的替代方案是 JSONPath。在 Java 中,有一个名为Jayway JsonPath 的实现,它与 Jackson 集成。

要使用coordinatesJsonPath 查询节点,您可以使用以下表达式:

$.address.coordinates
Run Code Online (Sandbox Code Playgroud)

查询节点的代码如下:

JsonNode coordinatesNode = JsonPath.parse(json)
                                   .read("$.address.coordinates", JsonNode.class);
Run Code Online (Sandbox Code Playgroud)

JsonPath 需要以下依赖项:

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.2.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

并且,要与 Jackson 集成,需要以下几行:

Configuration.setDefaults(new Configuration.Defaults() {

    private final JsonProvider jsonProvider = new JacksonJsonProvider();
    private final MappingProvider mappingProvider = new JacksonMappingProvider();

    @Override
    public JsonProvider jsonProvider() {
        return jsonProvider;
    }

    @Override
    public MappingProvider mappingProvider() {
        return mappingProvider;
    }

    @Override
    public Set<Option> options() {
        return EnumSet.noneOf(Option.class);
    }
});
Run Code Online (Sandbox Code Playgroud)

根据您的要求进行更新

根据您在问题中提供的其他详细信息,我想说 JSONPath 将为您提供比 JSON 指针更大的灵活性。

您可以尝试以下操作:

  • 而不是/requester/id使用$.requester[*].id.
  • 而不是/requester/demographic使用$.requester[*].demographic.

这些表达式可以在线测试。查看以下资源:

并阅读Jayway JsonPath 文档以了解如何正确使用它。