Java 使用 Jackson 从 JSON 获取单个字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26190851/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 01:59:44  来源:igfitidea点击:

Get single field from JSON using Hymanson

javajsonHymanson

提问by Jakub M.

Given an arbitrary JSON I would like to get value of a single field contentType. How to do it with Hymanson?

给定一个任意 JSON,我想获取单个字段的值contentType。如何用Hyman逊做到这一点?

{
  contentType: "foo",
  fooField1: ...
}

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

Related

有关的

采纳答案by ccjmne

The Hymanson Way

Hyman逊之路

Considering that you don't have a POJO describing your data structure, you could simply do:

考虑到您没有描述数据结构的 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"));
}    


Addressing concerns in the comments section

在评论部分解决问题

If, however, you wish to not consume the entire source String, but simply access a specific property whose path you know, you'll have to write it yourself, leveraging a Tokeniser.

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



Actually, it's the weekend and I got time on my hands, so I could give you a head start: here's a basic one! It can run in strictmode and spew out sensible error messages, or be lenient and return Optional.emptywhen the request couldn't be fulfilled.

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

public static class JSONPath {

    protected static final JsonFactory JSON_FACTORY = new JsonFactory();

    private final List<JSONKey> 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<String> getWithin(final String json) throws IOException {
        return this.getWithin(json, false);
    }

    public Optional<String> getWithin(final String json, final boolean strict) throws IOException {
        try (final InputStream stream = new StringInputStream(json)) {
            return this.getWithin(stream, strict);
        }
    }

    public Optional<String> getWithin(final InputStream json) throws IOException {
        return this.getWithin(json, false);
    }

    public Optional<String> getWithin(final InputStream json, final boolean strict) throws IOException {
        return getValueAt(JSON_FACTORY.createParser(json), 0, strict);
    }

    protected Optional<String> 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<String, String>) 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<String, String>) 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);
        }
    }
}

Assuming the following JSON content:

假设以下 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"
  }
}

... you could use my JSONPathclass as follows:

...您可以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]

Keep in mind that it's basic. It doesn't coerce data types (every value it returns is a String) and only returns leaf nodes.

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

Actual test case

实际测试用例

It handles InputStreams, so you can test it against some giant JSON document and see that it's much faster than it would take your browser to download and display its contents:

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

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

Quick test

快速测试

Note I'm not re-using any already existing JSONPathor ObjectMapperso the results are inaccurate -- this is just a very rough comparison anyways:

请注意,我没有重新使用任何已经存在的结果,JSONPath否则ObjectMapper结果是不准确的——无论如何,这只是一个非常粗略的比较:

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 Hymanson and access a specific field",
                                        time(() -> new ObjectMapper()
                                                .readValue(url.openStream(), ObjectNode.class)
                                                .get("info").get("contact").get("email"))));
}

378ms to get 'info.contact.email' with JSONPath
756ms to just download the entire document otherwise
896ms to bluntly map it entirely with Hymanson and access a specific field

378 毫秒使用 JSONPath 获取“info.contact.email” 756
毫秒只下载整个文档,否则
896 毫秒直接将其完全映射到 Hymanson 并访问特定字段

回答by Prasad Khode

If you are using JSON jars in your application then the following code snippet is useful:

如果您在应用程序中使用 JSON jar,那么以下代码片段很有用:

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

and if you are using Gson jars then the same code will look like following:

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

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

回答by xyz

Another way is:

另一种方式是:

String json = "{\"contentType\": \"foo\", \"fooField1\": ... }";
JsonNode parent= new ObjectMapper().readTree(json);
String content = parent.get("contentType").asText();

回答by Rohan Kumar

Just want to update for 2019. I found the following easiest to impl:

只想更新 2019 年。我发现以下最容易实现:

//json can be file or String
JsonNode parent= new ObjectMapper().readTree(json);
String content = parent.path("contentType").asText();

I would suggest to use pathinstead of getas getthrows a NPE, where path returns with a default 0 or "", which is safer to work with if setting up the parsing correctly for 1st time.

我建议使用path而不是getasget抛出 NPE,其中路径返回默认值 0 或“”,如果第一次正确设置解析,则使用更安全。

My $0.02

我的 0.02 美元

回答by Vrajendra Singh Mandloi

my Problem is a bit tricky.. for below Object i need JSON String ..

我的问题有点棘手..对于下面的对象我需要JSON字符串..

Below is my class in which there are some String and one JSON object we need to persist in DB in a single table ..

下面是我的类,其中有一些 String 和一个 JSON 对象,我们需要将其保存在单个表中的 DB 中。

public class TestTransaction extends TestTransaction {
private String radarId;
private String exclusionReason;
private Timestamp eventTs;
private JSONObject drivingAttributes;

I am using an Object mapper to convert this object but it is giving me a Json inside Json.. that is wrong.. i need to convert inside JSON object to String using below mapper

我正在使用对象映射器来转换这个对象,但它在 Json 中给了我一个 Json .. 那是错误的.. 我需要使用下面的映射器将内部 JSON 对象转换为字符串

public static final ObjectMapper EXCLUSION_OBJECT_MAPPER = new ObjectMapper()
        .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'+0000'"))
        .setSerializationInclusion(JsonInclude.Include.NON_NULL);

invalid JSON is:

无效的 JSON 是:

    {
    "txnId": "Test1",
    "txnIdDomain": "TEST1",
    "tradeDate": "2019-06-13T08:59:33.000+0000",
    "txnVersion": 0,
    "txnRevision": 0,
    "radarId": "TEST2",
    "exclusionReason": "CurrencyFilter Exclusion reason",
    "eventTs": "2019-07-18T19:03:32.426+0000",
    "drivingAttributes": {
        "Contra currency id": "USD",
        "Dealt currency id": "CAD"
    },
    "validated": false
}

correct JSON shall be

正确的 JSON 应为

{
    "txnId": "Test1",
    "txnIdDomain": "TEST1",
    "tradeDate": "2019-06-13T08:59:33.000+0000",
    "txnVersion": 0,
    "txnRevision": 0,
    "radarId": "TEST2",
    "exclusionReason": "CurrencyFilter Exclusion reason",
    "eventTs": "2019-07-18T19:03:32.426+0000",
    "drivingAttributes": "Contra currency id:USD, Dealt currency id:CAD",
    "validated": false
}