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
Get single field from JSON using Hymanson
提问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 strict
mode and spew out sensible error messages, or be lenient and return Optional.empty
when 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 JSONPath
class 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 InputStream
s, 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:
它处理InputStream
s,因此您可以针对一些巨大的 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 JSONPath
or ObjectMapper
so 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 path
instead of get
as get
throws 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
而不是get
asget
抛出 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
}