如何使用Jackson在对象中包含原始JSON?

bhi*_*rom 98 java json inner-classes jackson

当使用Jackson(de)序列化对象时,我试图在Java对象中包含原始JSON.为了测试这个功能,我写了以下测试:

public static class Pojo {
    public String foo;

    @JsonRawValue
    public String bar;
}

@Test
public void test() throws JsonGenerationException, JsonMappingException, IOException {

    String foo = "one";
    String bar = "{\"A\":false}";

    Pojo pojo = new Pojo();
    pojo.foo = foo;
    pojo.bar = bar;

    String json = "{\"foo\":\"" + foo + "\",\"bar\":" + bar + "}";

    ObjectMapper objectMapper = new ObjectMapper();
    String output = objectMapper.writeValueAsString(pojo);
    System.out.println(output);
    assertEquals(json, output);

    Pojo deserialized = objectMapper.readValue(output, Pojo.class);
    assertEquals(foo, deserialized.foo);
    assertEquals(bar, deserialized.bar);
}
Run Code Online (Sandbox Code Playgroud)

代码输出以下行:

{"foo":"one","bar":{"A":false}}
Run Code Online (Sandbox Code Playgroud)

JSON正是我想要看的东西.不幸的是,在尝试将JSON读回到对象时,代码失败并出现异常.这是一个例外:

org.codehaus.jackson.map.JsonMappingException:无法在[来源:java.io.StringReader@d70d7a;中的START_OBJECT标记中反序列化java.lang.String的实例.line:1,column:13](通过参考链:com.tnal.prism.cobalt.gather.testing.Pojo ["bar"])

为什么杰克逊在一个方向上运作良好但在向另一个方向运转时失败?看起来它应该能够再次将自己的输出作为输入.我知道我正在尝试做的是非正统的(一般的建议是创建一个内部对象,bar其中有一个名为的属性A),但我根本不想与这个JSON交互.我的代码充当了这段代码的传递 - 我想要接受这个JSON并再次发回它而不会触及任何东西,因为当JSON更改时我不希望我的代码需要修改.

感谢您的建议.

编辑:使Pojo成为静态类,导致不同的错误.

Sta*_*Man 57

@JsonRawValue仅用于序列化,因为反向操作有点棘手.实际上,它被添加以允许注入预编码的内容.

我想有可能添加对反向的支持,虽然这很尴尬:内容必须被解析,然后重新写回"原始"形式,这可能是也可能不一样(因为字符引用)可能不同).这是一般情况.但也许这对某些问题是有意义的.

但我认为一个解决您的特定情况下,将指定类型为"java.lang.Object继承",因为这应该可以工作:序列化,字符串将被原样输出,和反序列化,它将被作为反序列化一张地图.实际上你可能想要有单独的getter/setter,如果是这样的话; getter将返回String以进行序列化(并且需要@JsonRawValue); 和setter将采用Map或Object.如果有意义的话,你可以将它重新编码为String.


yve*_*lem 49

@StaxMan回答之后,我做了以下作品,就像一个魅力:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

并且,为了忠实于最初的问题,这是工作测试:

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}
  }
}
Run Code Online (Sandbox Code Playgroud)


Roy*_*ove 33

我可以用自定义解串器要做到这一点(剪切和粘贴来自这里)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 惊人的多么简单.IMO这应该是官方的答案.我尝试了一个包含数组,子对象等的非常复杂的结构.也许你编辑你的答案并添加要反序列化的String成员应该由@JsonDeserialize注释(使用= KeepAsJsonDeserialzier.class).(并纠正你班级名称中的拼写错误;-) (5认同)
  • @xtrakBandit for Serialization,使用`@JsonRawValue` (3认同)

ana*_*gaf 15

@JsonSetter可能有所帮助.查看我的示例('data'应该包含未解析的JSON):

class Purchase
{
    String data;

    @JsonProperty("signature")
    String signature;

    @JsonSetter("data")
    void setData(JsonNode data)
    {
        this.data = data.toString();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 根据JsonNode.toString()文档,该方法将产生开发人员可读的节点表示形式。&lt;b&gt;或可能&lt;/ b&gt;不是有效的JSON。因此,这实际上是非常冒险的。 (2认同)

ska*_*man 5

这是你的内部类的问题。该类Pojo是测试类的非静态内部类,Jackson 无法实例化该类。所以它可以序列化,但不能反序列化。

像这样重新定义你的类:

public static class Pojo {
    public String foo;

    @JsonRawValue
    public String bar;
}
Run Code Online (Sandbox Code Playgroud)

注意添加static