25

浏览文档和源代码,我没有看到一个明确的方法来做到这一点。好奇我是否遗漏了什么。

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

{"iamValidJson":"yay"}

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

Some text that is not JSON

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

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

有没有办法做到这一点?

4

5 回答 5

23

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

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

于 2013-05-30T00:11:46.507 回答
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;
    }
}

关于在自定义反序列化器中使用默认反序列化器的信息是如何从杰克逊的自定义反序列化器中调用默认反序列化器

于 2014-12-09T23:14:18.297 回答
4

构建我自己的反序列化器,我想在其中将特定字段反序列化为文本 iso 一个适当的 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 字符串。

于 2018-09-13T15:04:10.827 回答
4

晚了 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 字符串以用于记录目的,这就是我首先这样做的原因。很头疼,但终于做到了,我希望我能帮助别人:)

于 2018-05-02T05:37:16.057 回答
2

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

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
}
于 2013-05-31T05:37:53.003 回答