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

时间:2016-08-07 17:55:36

标签: json spring-boot jackson

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

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

由于我使用Spring Boot作为我的服务,杰克逊JAR应该已经可用。

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

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

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

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);
    }
}   

我的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"
                                            }
                      }
                   ]

}

我有两个问题如下。

如果我传递“/ manager / userId”,“/ administrator / isSuperUser”(OR)“/ salesId”我可以获得预期值。

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

当我通过“/ requester / id”(OR)“/ requester / demographic”时,我期待以下内容。

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


 "requester" : [
                    {
                            "demographic" : {
                                                "firstName" : "hello",
                                                "lastName" : "hai"
                                             }
                     },
                     {
                            "demographic" : {
                                                "firstName" : "welcome",
                                                "lastName" : "user"
                                            }
                      }
                   ]

除了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;
}

当我通过“/ 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]

2 个答案:

答案 0 :(得分:2)

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
    }
  }
}

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

/address/coordinates

JSON指针和杰克逊

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");

可以将coordinates节点解析为bean:

Coordinates coordinates = mapper.treeToValue(coordinatesNode, Coordinates.class);

或者可以写成String

String coordinatesNodeAsString = mapper.writerWithDefaultPrettyPrinter()
                                       .writeValueAsString(coordinatesNode);

Jayway JsonPath

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

要使用JsonPath查询coordinates节点,可以使用以下表达式:

$.address.coordinates

查询节点的代码如下:

JsonNode coordinatesNode = JsonPath.parse(json)
                                   .read("$.address.coordinates", JsonNode.class);

JsonPath需要以下依赖项:

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.2.0</version>
</dependency>

而且,为了与杰克逊整合,需要以下几行:

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);
    }
});

根据您的要求进行更新

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

您可以尝试以下操作:

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

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

阅读Jayway JsonPath documentation以了解如何正确使用它。

答案 1 :(得分:0)

Jackson的JsonView注释应该允许您使用特定视图标记某些字段/ getter。以下内容应该有效。

public class Item {
    public static class LoggingView {}

    @JsonView(LoggingView.class)
    public int id;

    @JsonView(LoggingView.class)
    public String name;

    public byte[] data;
}

通过执行以下操作,您可以在不编写id的情况下撰写namedata

public class Something {
    private static final Logger logger = LoggerFactory.getLogger();
    private static final ObjectWriter loggingItemWriter = new ObjectMapper().writerWithView(Item.LoggingView.class);

    public void doSomething(Item item) {
        ...
        logger.info(loggingItemWriter.writeValueAsString(item));
        ...
    }
}