英文:
How to process String of concatenated JSON blobs into List of Strings with Jackson?
问题
我有一个包含连接在一起的不同结构的JSON对象的字符串,我想将其转换为一个字符串列表。例如,给定以下输入:
```json
{
"foo": "bar"
}{
"wibble": "wobble"
}
...我想要得到一个类似这样的List<String>
输出:
[{"foo":"bar"}, {"wibble":"wobble"}]
理想情况下,我希望在不自己实现JSON规范的情况下完成这个操作。我找到了一个使用Jackson的简单实现,如果我的初始字符串不包含额外的空白字符,则可以正常工作:
public List<String> extractJsonBlobs(String json) throws IOException {
if (json.length() == 0) return ImmutableList.of();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(json, JsonNode.class);
return new ImmutableList.Builder<String>()
.add(jsonNode.toString())
.addAll(extractJsonBlobs(
json.substring(jsonNode.toString().length())
))
.build();
}
// 这个输入按描述工作
String workingString = "{\"foo\":\"bar\"}{\"wibble\":\"wobble\"}";
// 这个输入失败
String problemString = "{\n" +
" \"foo\": \"bar\"\n" +
"}{\n" +
" \"wibble\": \"wobble\"\n" +
"}";
问题在于jsonNode.toString().length()
始终给出了最小可能的表示长度(在这种情况下,第一个对象为13),而我需要的是连接的JSON对象的原始未处理字符串长度(在这种情况下,第一个对象为18)。我如何获取那个“原始”字符串长度,或者以其他方式遍历这些JSON对象呢?谢谢!
<details>
<summary>英文:</summary>
I have a String that contains concatenated JSON objects of varying structure that I want to turn into a list of strings. For example, given this input:
{
"foo": "bar"
}{
"wibble": "wobble"
}
...I would like to have a `List<String>` object output that looks like this:
[{"foo":"bar"}, {"wibble":"wobble"}]
Ideally I want to do this without implementing the JSON specification myself. I have found a naive implementation using Jackson that works if my initial string does **not** contain extra whitespace:
```java
public List<String> extractJsonBlobs(String json) throws IOException {
if (json.length() == 0) return ImmutableList.of();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(json, JsonNode.class);
return new ImmutableList.Builder<String>()
.add(jsonNode.toString())
.addAll(extractJsonBlobs(
json.substring(jsonNode.toString().length())
))
.build();
}
// this input works as described
String workingString = "{\"foo\":\"bar\"}{\"wibble\":\"wobble\"}";
// this input fails
String problemString = "{\n" +
" \"foo\": \"bar\"\n" +
"}{\n" +
" \"wibble\": \"wobble\"\n" +
"}";
The issue is that jsonNode.toString().length()
always gives me the smallest representation possible (13 for the first object in this case) when what I need is the original unprocessed string length for the concatenated JSON object (18 for the first object in this case). How can I either get that "original" string length or otherwise iterate through these JSON objects?
Thanks!
答案1
得分: 0
我采取的解决方案涉及使用解析器(而不仅仅使用ObjectMapper
尝试此操作),但使用JsonNode
,并再次依赖其toString()
行为来为我提供JSON字符串表示:
public List<String> extractJsonBlobs(String json) throws IOException {
JsonFactory factory = new JsonFactory(new ObjectMapper());
JsonParser parser = factory.createParser(json);
Iterator<JsonNode> messages = parser.readValuesAs(JsonNode.class);
return Streams.stream(messages)
.map(m -> m.toString())
.collect(toList());
}
英文:
The solution I landed on involved using a parser (instead of trying this only with ObjectMapper
), but using JsonNode
and again relying on its toString()
behavior to give me a JSON string representation:
public List<String> extractJsonBlobs(String json) throws IOException {
JsonFactory factory = new JsonFactory(new ObjectMapper());
JsonParser parser = factory.createParser(json);
Iterator<JsonNode> messages = parser.readValuesAs(JsonNode.class);
return Streams.stream(messages)
.map(m -> m.toString())
.collect(toList());
}
答案2
得分: 0
这是另一种解决方案:
public List<String> extractJsonBlobs(String json) {
JsonMapper mapper = new JsonMapper();
List<String> jsonBlobs = new ArrayList<>();
MappingIterator<JsonNode> it = mapper.readerFor(JsonNode.class)
.readValues(json)) {
while (it.hasNextValue()) {
jsonBlobs.add(it.nextValue().toString());
}
return jsonBlobs;
}
你可以对JsonEOFException进行try-catch处理,以防数据不完整(例如来自流),以便在缺少数据的情况下添加一个缓冲区:
public List<String> extractJsonBlobs(String json) {
JsonMapper mapper = new JsonMapper();
List<String> jsonBlobs = new ArrayList<>();
int lastSuccessfulParseCharLocation = 0;
try (MappingIterator<JsonNode> it = mapper.readerFor(JsonNode.class)
.readValues(json)) {
while (it.hasNextValue()) {
jsonBlobs.add(it.nextValue().toString());
lastSuccessfulParseCharLocation = (int) it.getCurrentLocation().getCharOffset();
}
} catch (JsonEOFException e) {
charBuffer.position(lastSuccessfulParseCharLocation);
Arrays.fill(prependChars, '\0');
charBuffer.get(prependChars, 0, (charBuffer.length()));
} catch (IOException e) {
throw new RuntimeException(e);
}
return jsonBlobs;
}
为了上下文,charBuffer是CharBuffer
,prependChars是char[]
。
英文:
Here's another solution:
public List<String> extractJsonBlobs(String json) {
JsonMapper mapper = new JsonMapper();
List<String> jsonBlobs = new ArrayList<>();
MappingIterator<JsonNode> it = mapper.readerFor(JsonNode.class)
.readValues(json)) {
while (it.hasNextValue()) {
jsonBlobs.add(it.nextValue().toString());
}
return jsonBlobs;
}
You can try-catch it for JsonEOFException in case that the data is not complete (e.g. coming from a stream) to prepend a buffer with the missing data :
public List<String> extractJsonBlobs(String json) {
JsonMapper mapper = new JsonMapper();
List<String> jsonBlobs = new ArrayList<>();
int lastSuccessfulParseCharLocation = 0;
try (MappingIterator<JsonNode> it = mapper.readerFor(JsonNode.class)
.readValues(json)) {
while (it.hasNextValue()) {
jsonBlobs.add(it.nextValue().toString());
lastSuccessfulParseCharLocation = (int) it.getCurrentLocation().getCharOffset();
}
} catch (JsonEOFException e) {
charBuffer.position(lastSuccessfulParseCharLocation);
Arrays.fill(prependChars, '\0');
charBuffer.get(prependChars, 0, (charBuffer.length()));
} catch (IOException e) {
throw new RuntimeException(e);
}
return jsonBlobs;
}
For context, charBuffer is a CharBuffer
and prependChars is a char[]
专注分享java语言的经验与见解,让所有开发者获益!
评论