如何从JsonParser获取底层字符串(Jackson Json)

问题描述 投票:16回答:5

浏览文档和源代码,我没有看到明确的方法来做到这一点。好奇,如果我错过了什么。

假设我从服务器响应中收到一个InputStream。我从这个InputStream创建一个JsonParser。预计服务器响应是包含有效JSON的文本,例如:

{"iamValidJson":"yay"}

但是,如果响应最终是无效的JSON或根本不是JSON,例如:

Some text that is not JSON

JsonParser最终会抛出异常。在这种情况下,我希望能够从JsonParser中提取底层的无效文本“Some text that is not JSON”,以便它可以用于其他目的。

我无法将其从InputStream中拉出来,因为它不支持重置,并且JsonParser的创建会消耗它。

有没有办法做到这一点?

java android json jackson inputstream
5个回答
18
投票

如果你有JsonParser那么你可以使用jsonParser.readValueAsTree().toString()

但是,这可能要求解析的JSON确实是有效的JSON。


5
投票

我有一个使用自定义反序列化器的情况,但我希望默认的反序列化器完成大部分工作,然后使用SAME json做一些额外的自定义工作。但是,在默认的反序列化器完成其工作后,JsonParser对象的当前位置超出了我需要的json文本。所以我遇到了和你一样的问题:如何访问底层的json字符串。

您可以使用JsonParser.getCurrentLocation.getSourceRef()来访问底层的json源。使用JsonParser.getCurrentLocation().getCharOffset()查找json源中的当前位置。

这是我使用的解决方案:

public class WalkStepDeserializer extends StdDeserializer<WalkStep> implements
    ResolvableDeserializer {

    // constructor, logger, and ResolvableDeserializer methods not shown

    @Override
    public MyObj deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        MyObj myObj = null;

        JsonLocation startLocation = jp.getCurrentLocation();
        long charOffsetStart = startLocation.getCharOffset();

        try {
            myObj = (MyObj) defaultDeserializer.deserialize(jp, ctxt);
        } catch (UnrecognizedPropertyException e) {
            logger.info(e.getMessage());
        }

        JsonLocation endLocation = jp.getCurrentLocation();
        long charOffsetEnd = endLocation.getCharOffset();
        String jsonSubString = endLocation.getSourceRef().toString().substring((int)charOffsetStart - 1, (int)charOffsetEnd);
        logger.info(strWalkStep);

        // Special logic - use JsonLocation.getSourceRef() to get and use the entire Json
        // string for further processing

        return myObj;
    }
}

有关在自定义反序列化器中使用默认反序列化器的信息,请访问How do I call the default deserializer from a custom deserializer in Jackson


2
投票

你要做的是超出Jackson的范围(大多数,如果不是所有其他Java JSON库那么)。您要做的是将输入流完全消耗为字符串,然后尝试使用Jackson将该字符串转换为JSON对象。如果转换失败,则使用中间字符串执行某些操作,否则正常进行。这是一个例子,它使用了优秀的Apache Commons IO library,方便:

final InputStream stream ; // Your stream here

final String json = IOUtils.toString(stream);
try {
    final JsonNode node = new ObjectMapper().readTree(json);
    // Do something with JSON object here
} catch(final JsonProcessingException jpe) {
    // Do something with intermediate string here
}

2
投票

晚了5年,但这是我的解决方案:

我将jsonParser转换为字符串

String requestString = jsonParser.readValueAsTree().toString();

然后我将该字符串转换为JsonParser

JsonFactory factory = new JsonFactory();
JsonParser parser  = factory.createParser(requestString);

然后我遍历我的解析器

ObjectMapper objectMapper = new ObjectMapper();
while(!parser.isClosed()){
    JsonToken jsonToken = parser.nextToken();
    if(JsonToken.FIELD_NAME.equals(jsonToken)){
        String currentName = parser.getCurrentName();
        parser.nextToken();
        switch (currentName) {
            case "someObject":
                Object someObject = objectMapper.readValue(parser, Object.class)
                //validate someObject
                break;
        }
 }

我需要保存原始的json字符串以便进行日志记录,这就是为什么我首先要这样做的原因。很难找到,但终于做到了,我希望我帮助别人:)


1
投票

构建我自己的反序列化器,我想在其中将特定字段反序列化为文本i.s.o.一个合适的DTO,这是我提出的解决方案。

我写了这样的自己的JsonToStringDeserializer:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringEscapeUtils;

import java.io.IOException;

/**
 * Deserialiser to deserialise any Json content to a String.
 */
@NoArgsConstructor
public class JsonToStringDeserializer extends JsonDeserializer<String> {


    /**
     * Deserialise a Json attribute that is a fully fledged Json object, into a {@link String}.
     * @param jsonParser Parsed used for reading JSON content
     * @param context Context that can be used to access information about this deserialization activity.
     * @return The deserialized value as a {@link String}.
     * @throws IOException
     */
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {

        final TreeNode node = jsonParser.getCodec().readTree(jsonParser);

        final String unescapedString = StringEscapeUtils.unescapeJava(node.toString());
        return unescapedString.substring(1, unescapedString.length()-1);
    }
}

注释要反序列化的字段,如下所示:

@JsonDeserialize(using = JsonToStringDeserializer.class)

我最初遵循的建议说使用这样的TreeNode:

    final TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
    return treeNode.toString();

但是你得到一个包含转义字符的Json字符串。

© www.soinside.com 2019 - 2024. All rights reserved.