Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


public static class Pojo {
    public String foo;

    @JsonRawValue
    public String bar;
}

Note the addition of static

Thanks. That got me one step further, but now I'm getting a different error. I'll update the original post with the new error.

This is a problem with your inner classes. The Pojo class is a non-static inner class of your test class, and Jackson cannot instantiate that class. So it can serialize, but not deserialize.

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


public class Pojo {
  Object json;

  @JsonRawValue
  public String getJson() {
    // default raw value: null or "[]"
    return json == null ? null : json.toString();
  }

  public void setJson(JsonNode node) {
    this.json = node;
  }
}
public class PojoTest {
  ObjectMapper mapper = new ObjectMapper();

  @Test
  public void test() throws IOException {
    Pojo pojo = new Pojo("{\"foo\":18}");

    String output = mapper.writeValueAsString(pojo);
    assertThat(output).isEqualTo("{\"json\":{\"foo\":18}}");

    Pojo deserialized = mapper.readValue(output, Pojo.class);
    assertThat(deserialized.json.toString()).isEqualTo("{\"foo\":18}");
    // deserialized.json == {"foo":18}
  }
}

And, to be faithful to the initial question, here is the working test:

Following @StaxMan answer, I've made the following works like a charm:

I didn't try, but it should work: 1) make a JsonNode node instead of Object json 2) use node.asText() instead of toString(). I am not sure about the 2nd one though.

I wonder why getJson() does return a String after all. If it just returned the JsonNode that was set through the setter, it would be serialized as desired, no?

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


@JsonRawValue is intended for serialization-side only, since the reverse direction is a bit trickier to handle. In effect it was added to allow injecting pre-encoded content.

@Sid there is no way to do that AND tokenization both efficiently. To support pass-through of unprocessed tokens would require additional state-keeping, which makes "regular" parsing somewhat less efficient. It is sort of like optimization between regular code and exception throwing: to support latter adds overhead for former. Jackson has not been designed to try to keep unprocessed input available; it'd be nice to have it (for error messages, too) around, but would require different approach.

But I think a work-around for your specific case would be to specify type as 'java.lang.Object', since this should work ok: for serialization, String will be output as is, and for deserialization, it will be deserialized as a Map. Actually you might want to have separate getter/setter if so; getter would return String for serialization (and needs @JsonRawValue); and setter would take either Map or Object. You could re-encode it to a String if that makes sense.

I guess it would be possible to add support for reverse, although that would be quite awkward: content will have to be parsed, and then re-written back to "raw" form, which may or may not be the same (since character quoting may differ). This for general case. But perhaps it would make sense for some subset of problems.

I had a different use case for this. Seems like if we don't want to generate a lot of string garbage in the deser/ser, we should be able to just passthrough a string as such. I saw a thread that tracked this, but it seems there is no native support possible. Have a look at markmail.org/message/

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


Check out the last answer. By defining a custom setter for the property that takes a JsonNode as parameter and calls the toString method on the jsonNode to set the String property, it all works out.

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


@JsonRawValue is intended for serialization-side only, since the reverse direction is a bit trickier to handle. In effect it was added to allow injecting pre-encoded content.

@Sid there is no way to do that AND tokenization both efficiently. To support pass-through of unprocessed tokens would require additional state-keeping, which makes "regular" parsing somewhat less efficient. It is sort of like optimization between regular code and exception throwing: to support latter adds overhead for former. Jackson has not been designed to try to keep unprocessed input available; it'd be nice to have it (for error messages, too) around, but would require different approach.

But I think a work-around for your specific case would be to specify type as 'java.lang.Object', since this should work ok: for serialization, String will be output as is, and for deserialization, it will be deserialized as a Map. Actually you might want to have separate getter/setter if so; getter would return String for serialization (and needs @JsonRawValue); and setter would take either Map or Object. You could re-encode it to a String if that makes sense.

I guess it would be possible to add support for reverse, although that would be quite awkward: content will have to be parsed, and then re-written back to "raw" form, which may or may not be the same (since character quoting may differ). This for general case. But perhaps it would make sense for some subset of problems.

I had a different use case for this. Seems like if we don't want to generate a lot of string garbage in the deser/ser, we should be able to just passthrough a string as such. I saw a thread that tracked this, but it seems there is no native support possible. Have a look at markmail.org/message/

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


import com.fasterxml.jackson.databind.Module;

@Component
public class ModuleImpl extends Module {

    @Override
    public void setupModule(SetupContext context) {
        context.addBeanDeserializerModifier(new BeanDeserializerModifierImpl());
    }
}
import java.util.Iterator;

import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;

public class BeanDeserializerModifierImpl extends BeanDeserializerModifier {
    @Override
    public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) {
        Iterator<SettableBeanProperty> it = builder.getProperties();
        while (it.hasNext()) {
            SettableBeanProperty p = it.next();
            if (p.getAnnotation(JsonRawValue.class) != null) {
                builder.addOrReplaceProperty(p.withValueDeserializer(KeepAsJsonDeserialzier.INSTANCE), true);
            }
        }
        return builder;
    }
}

Adding to Roy Truelove's great answer, this is how to inject the custom deserialiser in response to appearance of @JsonRawValue:

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


public static class Pojo {
    public String foo;

    @JsonRawValue
    public String bar;
}

Note the addition of static

Thanks. That got me one step further, but now I'm getting a different error. I'll update the original post with the new error.

This is a problem with your inner classes. The Pojo class is a non-static inner class of your test class, and Jackson cannot instantiate that class. So it can serialize, but not deserialize.

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


@JsonRawValue
package etc;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

/**
 * Keeps json value as json, does not try to deserialize it
 * @author roytruelove
 *
 */
public class KeepAsJsonDeserialzier extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        TreeNode tree = jp.getCodec().readTree(jp);
        return tree.toString();
    }
}

Amazing how simple. IMO this should be the official answer. I tried with a very complex structure containing arrays, subobjects, etc. Maybe you edit your answer and add that the String member to be deserialized should be annotated by @JsonDeserialize( using = KeepAsJsonDeserialzier.class ). (and correct the typo in your class name ;-)

I was able to do this with a custom deserializer (cut and pasted from here)

This works like a charm. Thank you Roy and @Heri ..combination of this post together with Heri's comment is imho the best answer.

this works for Deserializion. How about for Serialization of raw json into a pojo? How would that be accomplished

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


public class Pojo {
  Object json;

  @JsonRawValue
  public String getJson() {
    // default raw value: null or "[]"
    return json == null ? null : json.toString();
  }

  public void setJson(JsonNode node) {
    this.json = node;
  }
}
public class PojoTest {
  ObjectMapper mapper = new ObjectMapper();

  @Test
  public void test() throws IOException {
    Pojo pojo = new Pojo("{\"foo\":18}");

    String output = mapper.writeValueAsString(pojo);
    assertThat(output).isEqualTo("{\"json\":{\"foo\":18}}");

    Pojo deserialized = mapper.readValue(output, Pojo.class);
    assertThat(deserialized.json.toString()).isEqualTo("{\"foo\":18}");
    // deserialized.json == {"foo":18}
  }
}

And, to be faithful to the initial question, here is the working test:

Following @StaxMan answer, I've made the following works like a charm:

I didn't try, but it should work: 1) make a JsonNode node instead of Object json 2) use node.asText() instead of toString(). I am not sure about the 2nd one though.

I wonder why getJson() does return a String after all. If it just returned the JsonNode that was set through the setter, it would be serialized as desired, no?

Note
Rectangle 27 0

java How can I include raw JSON in an object using Jackson?


@JsonRawValue
package etc;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

/**
 * Keeps json value as json, does not try to deserialize it
 * @author roytruelove
 *
 */
public class KeepAsJsonDeserialzier extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        TreeNode tree = jp.getCodec().readTree(jp);
        return tree.toString();
    }
}

Amazing how simple. IMO this should be the official answer. I tried with a very complex structure containing arrays, subobjects, etc. Maybe you edit your answer and add that the String member to be deserialized should be annotated by @JsonDeserialize( using = KeepAsJsonDeserialzier.class ). (and correct the typo in your class name ;-)

I was able to do this with a custom deserializer (cut and pasted from here)

This works like a charm. Thank you Roy and @Heri ..combination of this post together with Heri's comment is imho the best answer.

this works for Deserializion. How about for Serialization of raw json into a pojo? How would that be accomplished

Note