如何使用Jackson将连接的JSON块字符串处理成字符串列表?

huangapple 未分类评论46阅读模式
英文:

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&lt;String&gt;` 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&lt;String&gt; 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&lt;String&gt;()
            .add(jsonNode.toString())
            .addAll(extractJsonBlobs(
                    json.substring(jsonNode.toString().length())
            ))
            .build();
}
// this input works as described
String workingString = &quot;{\&quot;foo\&quot;:\&quot;bar\&quot;}{\&quot;wibble\&quot;:\&quot;wobble\&quot;}&quot;;

// this input fails
String problemString = &quot;{\n&quot; +
        &quot;  \&quot;foo\&quot;: \&quot;bar\&quot;\n&quot; +
        &quot;}{\n&quot; +
        &quot;  \&quot;wibble\&quot;: \&quot;wobble\&quot;\n&quot; +
        &quot;}&quot;;

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&lt;String&gt; extractJsonBlobs(String json) throws IOException {
        JsonFactory factory = new JsonFactory(new ObjectMapper());
        JsonParser parser = factory.createParser(json);
        Iterator&lt;JsonNode&gt; messages = parser.readValuesAs(JsonNode.class);

        return Streams.stream(messages)
                .map(m -&gt; 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&lt;String&gt; extractJsonBlobs(String json) {

        JsonMapper mapper = new JsonMapper();
        List&lt;String&gt; jsonBlobs = new ArrayList&lt;&gt;();

        MappingIterator&lt;JsonNode&gt; 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&lt;String&gt; extractJsonBlobs(String json) {

        JsonMapper mapper = new JsonMapper();
        List&lt;String&gt; jsonBlobs = new ArrayList&lt;&gt;();

        int lastSuccessfulParseCharLocation = 0;

        try (MappingIterator&lt;JsonNode&gt; 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, &#39;\0&#39;);
            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[]

huangapple
  • 本文由 发表于 2020年4月7日 09:23:56
  • 转载请务必保留本文链接:https://java.coder-hub.com/61071381.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定