How to get the underlying String from a JsonParser (Jackson Json)

后端 未结 5 2162
囚心锁ツ
囚心锁ツ 2021-02-18 15:23

Looking through the documentation and source code I don\'t see a clear way to do this. Curious if I\'m missing something.

Say I receive an InputStream from a server resp

相关标签:
5条回答
  • 2021-02-18 15:50

    I had a situation where I was using a custom deserializer, but I wanted the default deserializer to do most of the work, and then using the SAME json do some additional custom work. However, after the default deserializer does its work, the JsonParser object current location was beyond the json text I needed. So I had the same problem as you: how to get access to the underlying json string.

    You can use JsonParser.getCurrentLocation.getSourceRef() to get access to the underlying json source. Use JsonParser.getCurrentLocation().getCharOffset() to find the current location in the json source.

    Here's the solution I used:

    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;
        }
    }
    

    And info about using a default deserializer in a custom deserializer is at How do I call the default deserializer from a custom deserializer in Jackson

    0 讨论(0)
  • 2021-02-18 15:53

    What you are trying to do is outside the scope of Jackson (and most, if not all other Java JSON libraries out there). What you want to do is fully consume the input stream into a string, then attempt to convert that string to a JSON object using Jackson. If the conversion fails then do something with the intermediate string, else proceed normally. Here's an example, which utilizes the excellent Apache Commons IO library, for convenience:

    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
    }
    
    0 讨论(0)
  • 2021-02-18 15:55

    Building my own Deserialiser in which I wanted to deserialise a specific field as text i.s.o. a proper DTO, this is the solution I came up with.

    I wrote my own JsonToStringDeserializer like this:

    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);
        }
    }
    

    Annotate the field you want to deserialize like this:

    @JsonDeserialize(using = JsonToStringDeserializer.class)
    

    I initially followed advice that said to use a TreeNode like this:

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

    But then you get a Json String that contains escape characters.

    0 讨论(0)
  • 2021-02-18 16:02

    5 year late, but this was my solution:

    I converted the jsonParser to string

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

    Then I converted that string into a JsonParser

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

    Then I iterated through my parser

    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;
            }
     }
    

    I needed to save the original json string for logging purposes, which is why I did this in the first place. Was a headache to find out, but finally did it and I hope i'm helping someone out :)

    0 讨论(0)
  • 2021-02-18 16:07

    If you have the JsonParser then you can use jsonParser.readValueAsTree().toString().

    However, this likely requires that the JSON being parsed is indeed valid JSON.

    0 讨论(0)
提交回复
热议问题