使用Jackson从JSON获取单个字段

给定一个任意的JSON,我想获得单个字段contentType值。 如何与jackson一起做?

 { contentType: "foo", fooField1: ... } { contentType: "bar", barArray: [...] } 

有关

  • 如何从Java中的JSON-string中查找指定的名称及其值? (GSON)
  • 使用gson反序列化对象的特定JSON字段 (GSON)

jackson之路

考虑到您没有描述数据结构的POJO,您可以简单地执行以下操作:

 final String json = "{\"contentType\": \"foo\", \"fooField1\": ... }"; final ObjectNode node = new ObjectMapper().readValue(json, ObjectNode.class); // ^ // actually, try and *reuse* a single instance of ObjectMapper if (node.has("contentType")) { System.out.println("contentType: " + node.get("contentType")); } 

解决评论部分中的问题

但是,如果您不想使用整个源String ,而只是访问您知道其路径的特定属性,则必须自己编写,并利用Tokeniser。


实际上,这是周末,我有时间在我的手上,所以我可以给你一个良好的开端:这是一个基本的! 它可以在strict模式下运行并发出合理的错误消息,或者是宽松的,并在无法满足请求时返回Optional.empty

 public static class JSONPath { protected static final JsonFactory JSON_FACTORY = new JsonFactory(); private final List keys; public JSONPath(final String from) { this.keys = Arrays.stream((from.startsWith("[") ? from : String.valueOf("." + from)) .split("(?=\\[|\\]|\\.)")) .filter(x -> !"]".equals(x)) .map(JSONKey::new) .collect(Collectors.toList()); } public Optional getWithin(final String json) throws IOException { return this.getWithin(json, false); } public Optional getWithin(final String json, final boolean strict) throws IOException { try (final InputStream stream = new StringInputStream(json)) { return this.getWithin(stream, strict); } } public Optional getWithin(final InputStream json) throws IOException { return this.getWithin(json, false); } public Optional getWithin(final InputStream json, final boolean strict) throws IOException { return getValueAt(JSON_FACTORY.createParser(json), 0, strict); } protected Optional getValueAt(final JsonParser parser, final int idx, final boolean strict) throws IOException { try { if (parser.isClosed()) { return Optional.empty(); } if (idx >= this.keys.size()) { parser.nextToken(); if (null == parser.getValueAsString()) { throw new JSONPathException("The selected node is not a leaf"); } return Optional.of(parser.getValueAsString()); } this.keys.get(idx).advanceCursor(parser); return getValueAt(parser, idx + 1, strict); } catch (final JSONPathException e) { if (strict) { throw (null == e.getCause() ? new JSONPathException(e.getMessage() + String.format(", at path: '%s'", this.toString(idx)), e) : e); } return Optional.empty(); } } @Override public String toString() { return ((Function) x -> x.startsWith(".") ? x.substring(1) : x) .apply(this.keys.stream().map(JSONKey::toString).collect(Collectors.joining())); } private String toString(final int idx) { return ((Function) x -> x.startsWith(".") ? x.substring(1) : x) .apply(this.keys.subList(0, idx).stream().map(JSONKey::toString).collect(Collectors.joining())); } @SuppressWarnings("serial") public static class JSONPathException extends RuntimeException { public JSONPathException() { super(); } public JSONPathException(final String message) { super(message); } public JSONPathException(final String message, final Throwable cause) { super(message, cause); } public JSONPathException(final Throwable cause) { super(cause); } } private static class JSONKey { private final String key; private final JsonToken startToken; public JSONKey(final String str) { this(str.substring(1), str.startsWith("[") ? JsonToken.START_ARRAY : JsonToken.START_OBJECT); } private JSONKey(final String key, final JsonToken startToken) { this.key = key; this.startToken = startToken; } /** * Advances the cursor until finding the current {@link JSONKey}, or * having consumed the entirety of the current JSON Object or Array. */ public void advanceCursor(final JsonParser parser) throws IOException { final JsonToken token = parser.nextToken(); if (!this.startToken.equals(token)) { throw new JSONPathException(String.format("Expected token of type '%s', got: '%s'", this.startToken, token)); } if (JsonToken.START_ARRAY.equals(this.startToken)) { // Moving cursor within a JSON Array for (int i = 0; i != Integer.valueOf(this.key).intValue(); i++) { JSONKey.skipToNext(parser); } } else { // Moving cursor in a JSON Object String name; for (parser.nextToken(), name = parser.getCurrentName(); !this.key.equals(name); parser.nextToken(), name = parser.getCurrentName()) { JSONKey.skipToNext(parser); } } } /** * Advances the cursor to the next entry in the current JSON Object * or Array. */ private static void skipToNext(final JsonParser parser) throws IOException { final JsonToken token = parser.nextToken(); if (JsonToken.START_ARRAY.equals(token) || JsonToken.START_OBJECT.equals(token) || JsonToken.FIELD_NAME.equals(token)) { skipToNextImpl(parser, 1); } else if (JsonToken.END_ARRAY.equals(token) || JsonToken.END_OBJECT.equals(token)) { throw new JSONPathException("Could not find requested key"); } } /** * Recursively consumes whatever is next until getting back to the * same depth level. */ private static void skipToNextImpl(final JsonParser parser, final int depth) throws IOException { if (depth == 0) { return; } final JsonToken token = parser.nextToken(); if (JsonToken.START_ARRAY.equals(token) || JsonToken.START_OBJECT.equals(token) || JsonToken.FIELD_NAME.equals(token)) { skipToNextImpl(parser, depth + 1); } else { skipToNextImpl(parser, depth - 1); } } @Override public String toString() { return String.format(this.startToken.equals(JsonToken.START_ARRAY) ? "[%s]" : ".%s", this.key); } } } 

假设以下JSON内容:

 { "people": [{ "name": "Eric", "age": 28 }, { "name": "Karin", "age": 26 }], "company": { "name": "Elm Farm", "address": "3756 Preston Street Wichita, KS 67213", "phone": "857-778-1265" } } 

…您可以使用我的JSONPath类,如下所示:

  final String json = "{\"people\":[],\"company\":{}}"; // refer to JSON above System.out.println(new JSONPath("people[0].name").getWithin(json)); // Optional[Eric] System.out.println(new JSONPath("people[1].name").getWithin(json)); // Optional[Karin] System.out.println(new JSONPath("people[2].name").getWithin(json)); // Optional.empty System.out.println(new JSONPath("people[0].age").getWithin(json)); // Optional[28] System.out.println(new JSONPath("company").getWithin(json)); // Optional.empty System.out.println(new JSONPath("company.name").getWithin(json)); // Optional[Elm Farm] 

请记住,这是基本的 。 它不强制数据类型(它返回的每个值都是一个String )并且只返回叶子节点。

实际测试用例

它处理InputStream ,因此您可以针对某些巨型JSON文档对其进行测试,并发现它比浏览器下载和显示其内容要快得多:

 System.out.println(new JSONPath("info.contact.email") .getWithin(new URL("http://test-api.rescuegroups.org/v5/public/swagger.php").openStream())); // Optional[support@rescuegroups.org] 

快速测试

注意我没有重复使用任何已经存在的JSONPathObjectMapper因此结果不准确 – 这只是一个非常粗略的比较:

 public static Long time(final Callable r) throws Exception { final long start = System.currentTimeMillis(); r.call(); return Long.valueOf(System.currentTimeMillis() - start); } public static void main(final String[] args) throws Exception { final URL url = new URL("http://test-api.rescuegroups.org/v5/public/swagger.php"); System.out.println(String.format( "%dms to get 'info.contact.email' with JSONPath", time(() -> new JSONPath("info.contact.email").getWithin(url.openStream())))); System.out.println(String.format( "%dms to just download the entire document otherwise", time(() -> new Scanner(url.openStream()).useDelimiter("\\A").next()))); System.out.println(String.format( "%dms to bluntly map it entirely with Jackson and access a specific field", time(() -> new ObjectMapper() .readValue(url.openStream(), ObjectNode.class) .get("info").get("contact").get("email")))); } 

378ms用JSONPath获取’info.contact.email’
否则只需756ms即可下载整个文档
896毫秒直接将它与jackson完全映射并访问特定的字段

如果您在应用程序中使用JSON jar,则以下代码段非常有用:

 String json = "{\"contentType\": \"foo\", \"fooField1\": ... }"; JSONObject jsonObject = new JSONObject(json); System.out.println(jsonObject.getString("contentType")); 

如果您使用的是Gson jar,那么相同的代码将如下所示:

 Gson gson = new GsonBuilder().create(); Map jsonMap = gson.fromJson(json, Map.class); System.out.println(jsonMap.get("contentType"));