【问题标题】:Is it possible to make Jackson serialize a nested object as a string是否可以让杰克逊将嵌套对象序列化为字符串
【发布时间】:2018-12-10 23:52:36
【问题描述】:

鉴于这些类:

@Value
private static class Message {
    private final String type;
    private final MyType message;
}

@Value
public class MyType {
    private final String foo;
}

杰克逊将产生:

{
  "Type" : "Test",
  "Message" : {"foo" : "bar"}
}

我可以给 Jackson 提供某种类型的注释或指令,要求它将嵌套的复杂类型序列化为字符串,例如所需的 JSON 将是:

{
  "Type" : "Test",
  "Message" : "{\"foo\" : \"bar\"}"
}

我在消息字段上尝试了这两个注释:

 @JsonFormat(shape = JsonFormat.Shape.STRING)
 @JsonSerialize(as=String.class)

两者都没有预期的影响。现在我的“hack”是在施工时做到这一点:

return new Message("Test", mapper.writeValueAsString(new MyType("bar")));

我想我可以编写一个自定义序列化程序,但我想知道这是否是某种内置的标准行为。我的用例是我正在构建一个 JSON 有效负载,它应该有一个字符串消息包含在它本身包含JSON

环境

Jackson 版本是 2.9.0,在 Java 10 上使用 Spring Boot 2。

【问题讨论】:

    标签: json jackson jackson2 jackson-databind


    【解决方案1】:

    可以使用自定义序列化器来完成:

    class EscapedJsonSerializer extends StdSerializer<Object> {
        public EscapedJsonSerializer() {
            super((Class<Object>) null);
        }
    
    
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            StringWriter str = new StringWriter();
            JsonGenerator tempGen = new JsonFactory().setCodec(gen.getCodec()).createGenerator(str);
            if (value instanceof Collection || value.getClass().isArray()) {
                tempGen.writeStartArray();
                if (value instanceof Collection) {
                    for (Object it : (Collection) value) {
                        writeTree(gen, it, tempGen);
                    }
                } else if (value.getClass().isArray()) {
                    for (Object it : (Object[]) value) {
                        writeTree(gen, it, tempGen);
                    }
                }
                tempGen.writeEndArray();
            } else {
                provider.defaultSerializeValue(value, tempGen);
            }
            tempGen.flush();
            gen.writeString(str.toString());
        }
    
    
        @Override
        public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
            StringWriter str = new StringWriter();
            JsonGenerator tempGen = new JsonFactory().setCodec(gen.getCodec()).createGenerator(str);
            writeTree(gen, value, tempGen);
            tempGen.flush();
            gen.writeString(str.toString());
        }
    
        private void writeTree(JsonGenerator gen, Object it, JsonGenerator tempGen) throws IOException {
            ObjectNode tree = ((ObjectMapper) gen.getCodec()).valueToTree(it);
            tree.set("@class", new TextNode(it.getClass().getName()));
            tempGen.writeTree(tree);
        }
    }
    

    和反序列化器:

    class EscapedJsonDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
        private final Map<JavaType, JsonDeserializer<Object>> cachedDeserializers = new HashMap<>();
    
        @Override
        public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            throw new IllegalArgumentException("EscapedJsonDeserializer should delegate deserialization for concrete class");
    
        }
    
        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
            JavaType type = (ctxt.getContextualType() != null) ?
                    ctxt.getContextualType() : property.getMember().getType();
            return cachedDeserializers.computeIfAbsent(type, (a) -> new InnerDeserializer(type));
        }
    
        private class InnerDeserializer extends JsonDeserializer<Object> {
            private final JavaType javaType;
    
            private InnerDeserializer(JavaType javaType) {
                this.javaType = javaType;
            }
    
            @Override
            public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
                String string = p.readValueAs(String.class);
                return ((ObjectMapper) p.getCodec()).readValue(string, javaType);
            }
    
            @Override
            public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
                    throws IOException {
    
                String str = p.readValueAs(String.class);
    
    
                TreeNode root = ((ObjectMapper) p.getCodec()).readTree(str);
                Class clz;
                try {
                    clz = Class.forName(((TextNode) root.get("@class")).asText());
                    Object newJsonNode = p.getCodec().treeToValue(root, clz);
                    return newJsonNode;
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    

    该字段应使用@JsonSerialize 和@JsonDeserialize 注释(如果需要)

    class Outer {
        @JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.CLASS)
        @JsonSerialize(using = EscapedJsonSerializer.class)
        @JsonDeserialize(using = EscapedJsonDeserializer.class)
        public Foo val;
    }
    

    它适用于简单的集合(列表、数组),并且在某种程度上适用于多态性,尽管对于特定的多态性相关问题可能需要更精细的解决方案。 示例输出如下所示:

    {"val":"{\"foo\":\"foo\",\"@class\":\"org.test.Foo\"}"}
    {"val":"{\"foo\":\"foo\",\"bar\":\"bar\",\"@class\":\"org.test.Bar\"}"}
    

    【讨论】:

      【解决方案2】:

      我也找不到内置解决方案,最终编写了自定义转换器:

      public class ObjectToJsonStringConverter extends StdConverter<Object, String> {
      
          private final ObjectMapper objectMapper = new ObjectMapper();
      
          @Override
          public String convert(Object value) {
              try {
                  return objectMapper.writeValueAsString(value);
              } catch (JsonProcessingException e) {
                  throw new IllegalStateException(e);
              }
          }
      }
      

      用法:

      @Value
      private static class Message {
          private final String type;
      
          @JsonSerialize(converter = ObjectToJsonStringConverter.class)
          private final MyType message;
      }
      

      【讨论】:

        猜你喜欢
        • 2017-09-22
        • 2016-01-14
        • 2016-12-07
        • 2019-11-15
        • 2021-10-02
        • 1970-01-01
        • 1970-01-01
        • 2016-10-28
        相关资源
        最近更新 更多