How to alter the value of a JsonNode while constructing it from a string in Jackson

dks551 :

I have a JSON string and I want to alter the value while constructing the JsonNode using Jackson library. eg:-

input: {"name":"xyz","price":"90.00"}
output:{"name":"xyz-3","price":90.90}

I created my own JsonFactory and passed my own Parser. but I can only alter the keys, not the values associated with a key.

code:

private static ObjectMapper create() {
        ObjectMapper objectMapper = new ObjectMapper(new JsonFactory() {
            @Override
            protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
                return new MyParser(super._createParser(data, offset, len, ctxt));
            }

            @Override
            protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
                return new MyParser(super._createParser(in, ctxt));
            }

            @Override
            protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
                return new MyParser(super._createParser(r, ctxt));
            }

            @Override
            protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable)
                    throws IOException {
                return new MyParser(super._createParser(data, offset, len, ctxt, recyclable));
            }
        });

private static final class MyParser extends JsonParserDelegate {

        private MyParser(JsonParser d) {
            super(d);
        }

        @Override
        public String getCurrentName() throws IOException, JsonParseException {
            ....
        }

        @Override
        public String getText() throws IOException, JsonParseException {
           ...
        }

        @Override
        public Object getCurrentValue() {
            ...
        }


        @Override
        public String getValueAsString() throws IOException {
            ...
        }

        @Override
        public String getValueAsString(String defaultValue) throws IOException {
            ...
        }
    }

Below is the code to construct the JsonNode from the string.

mapper.readTree(jsonStr);

In this case when the readTree method is called the getCurrentValue or getValueAsString methods are not called, so I am not able to alter the value while creating the JsonNode itself. Also the json strings can be different. Basically I want to construct a JsonNode from the string. so tying to a specific schema/bean is not a good choice here. How to address this ? TIA

Adding the updated code for version 2.7.4:-

static class MyParser extends JsonParserDelegate {
        MyParser(final JsonParser delegate) {
            super(delegate);
        }

        @Override
        public String getText() throws IOException {
            final String text = super.getText();

            if ("name".equals(getCurrentName())) {
                return text + "-3";
            }

            return text;
        }

        @Override
        public JsonToken nextToken() throws IOException {
            if ("price".equals(getCurrentName())) {
                // Advance token anyway
                super.nextToken();
                return JsonToken.VALUE_NUMBER_FLOAT;
            }

            return super.nextToken();
        }

        @Override
        public int getCurrentTokenId() {
            try {
                if ("price".equals(getCurrentName())) {
                    return JsonTokenId.ID_NUMBER_FLOAT;
                }
            } catch (final IOException e) {
                //
            }

            return super.getCurrentTokenId();
        }

        @Override
        public NumberType getNumberType() throws IOException {
            if ("price".equals(getCurrentName())) {
                return NumberType.FLOAT;
            }

            return super.getNumberType();
        }

        @Override
        public float getFloatValue() throws IOException {
            return Float.parseFloat(getValueAsString("0")) + 0.09F;
        }

        @Override
        public double getDoubleValue() throws IOException {
            return Double.parseDouble(getValueAsString("0")) + 0.09D;
        }


    }

pom.xml:-

         <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
            <version>2.8.7</version>
            <!--<scope>test</scope>-->
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.8.7</version>
        </dependency>
LppEdd :

Edit: there is a subtle difference between 2.7.* and 2.9.*.
While 2.9.* is able to differentiate between double and float with

getDoubleValue()
getFloatValue()

instead 2.7.* only uses

getDoubleValue()

even for ID_NUMBER_FLOAT tokens.
So, you need to decide if you want to maintain retro-compatibility or not.

You can also override both, like I did here.


This is all what you need for your custom MyParser

static class MyParser extends JsonParserDelegate {
    MyParser(final JsonParser delegate) {
        super(delegate);
    }

    @Override
    public String getText() throws IOException {
        final String text = super.getText();

        if ("name".equals(getCurrentName())) {
            return text + "-3";
        }

        return text;
    }

    @Override
    public JsonToken nextToken() throws IOException {
        if ("price".equals(getCurrentName())) {
            // Advance token anyway
            super.nextToken();
            return JsonToken.VALUE_NUMBER_FLOAT;
        }

        return super.nextToken();
    }

    @Override
    public int getCurrentTokenId() {
        try {
            if ("price".equals(getCurrentName())) {
                return JsonTokenId.ID_NUMBER_FLOAT;
            }
        } catch (final IOException e) {
            //
        }

        return super.getCurrentTokenId();
    }

    @Override
    public NumberType getNumberType() throws IOException {
        if ("price".equals(getCurrentName())) {
            return NumberType.FLOAT;
        }

        return super.getNumberType();
    }

    @Override
    public float getFloatValue() throws IOException {
        return Float.parseFloat(getValueAsString("0")) + 0.09F;
    }

    @Override
    public double getDoubleValue() throws IOException {
       return Double.parseDouble(getValueAsString("0")) + 0.09D;
    }
}

Output: {"name":"xyz-3","price":90.09}

Your code seems fine, and it's tested and working ;)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=100784&siteId=1