【问题标题】:Jackson @JsonUnwrapped behaviour with custom JsonSerializerJackson @JsonUnwrapped 行为与自定义 JsonSerializer
【发布时间】:2015-06-15 13:26:56
【问题描述】:

我有两个这样的课程:

public class A {
    String aProp = "aProp";

    public String getAProp() {
        return aProp;
    }
}

public class B {
    String bProp = "bProp";
    A a = new A();

    @JsonProperty("bProp")
    public String getBProp() {
        return bProp;
    }

    @JsonSerialize(using = CustomSerializer.class)
    public A getA() {
        return a;
    }     
}

我希望得到这样的 JSON:

{
    "bProp": "bProp",         // just serizlised bProp
    "sProp1": "sProp1_aProp", // computed using aProp
    "sProp2": "sProp2_aProp"  // computed another way
}

所以我这样写了自定义JsonSerializer

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class CustomSerializer extends JsonSerializer<A> {
    @Override
    public void serialize(A a, JsonGenerator json, SerializerProvider provider) throws IOException {
        json.writeStringField("sProp1", "sProp1_" + a.getAProp());
        json.writeStringField("sProp2", "sProp2_" + a.getAProp());
    }
}

但我不断收到错误消息:

com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value

除非我将json.writeStartObject();json.writeEndObject(); 放在序列化方法中(这样会产生错误的JSON)。

所以我正在寻找像 @JsonUnwrapped 这样的解决方案,以与自定义 JsonSerializer 一起使用。

【问题讨论】:

  • 请修正示例代码。您正在使用 JsonSerializer 来序列化 A 类型的字段。这没有多大意义。
  • @JBNizet,谢谢,已修复。

标签: java json jackson


【解决方案1】:

我了解您的问题,您需要的是UnwrappingBeanSerializer。您可以看到另一个相关的 SO 帖子: Different JSON output when using custom json serializer in Spring Data Rest

问题是你不能在一个字段中同时拥有@JacksonUnwrapped@JsonSerialize 的注解,因为当你拥有@JsonSerializer 时,Jackson 总是会写字段名。

这里是完整的解决方案:

public class CustomSerializer  extends UnwrappingBeanSerializer {
    public CustomSerializer(BeanSerializerBase src, NameTransformer transformer) {
        super(src, transformer);
    }

    @Override
    public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
        return new CustomSerializer(this, transformer);
    }

    @Override
    protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
        A a = (A) bean;
        jgen.writeStringField("custom", a.getAProp());
        jgen.writeStringField("custom3", a.getAProp());
    }

    @Override
    public boolean isUnwrappingSerializer() {
        return true;
    }

}

测试用例,您应该使用自定义配置重新定义您的对象映射器或研究其他方法。

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class)
public class ColorsTest {

    ObjectMapper mapper = new ObjectMapper();

    @Before
    public void setUp(){
        mapper.registerModule(new Module() {
            @Override
            public String getModuleName() {
                return "my.module";
            }

            @Override
            public Version version() {
                return Version.unknownVersion();
            }

            @Override
            public void setupModule(SetupContext context) {

                context.addBeanSerializerModifier(new BeanSerializerModifier() {
                    @Override
                    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
                        if(beanDesc.getBeanClass().equals(A.class)) {
                            return new CustomSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
                        }
                        return serializer;
                    }
                });

            }
        });
    }
    @Test
    public void testSerializer() throws JsonProcessingException {
        System.out.println(mapper.writeValueAsString(new B()));
    }
}

B类:

public class B {

        @JsonProperty("bProp")
        public String getBProp() {
            return "bProp";
        }


    @JsonUnwrapped
        public A getA() {
            return new A();
        }
}

【讨论】:

  • 谢谢,尼古拉。还有一件事:如何使 Jackson 模块与具体的 A 类无关?所以它不会检查beanDesc.getBeanClass().equals(A.class),而是检查属性上是否存在一些自定义注释?所以它会在序列化 B 中的 a 属性时使用 CustomSerializer,但如果它是另一个类或属性则不要。
  • fasterxml.github.io/jackson-databind/javadoc/2.2.0/com/… 这是文档 BeanDescription 有方法 getClassAnnotations() 你会得到它;)
  • 我不能使用getClassAnnotations(),我需要B 类中属性方法getA() 的注释,而不是A 类本身的注释。反射没有用。
  • 要访问属性或封闭类的注释,请实现ContextualSerializer; createContextual() 获取传递的属性定义,如果存在(序列化根值时不存在),可以通过 with 注释访问。这还包括混合、分层覆盖,因此理想情况下,您永远不需要通过MethodField 直接使用反射
【解决方案2】:

我喜欢将这篇文章和解决方案添加到此处提出的问题:Using custom Serializers with JsonUnwrapperd,因为原始发帖人使用的是 JsonSerializer。在这种情况下,UnwrappingBeanSerializer 的建议方法将不起作用。我的帖子的目标略有不同,但帖子中的想法应该很容易适用于您的用例,因为它只是覆盖了另外一个方法,而不必在属性上添加除 JsonUnwrapped 之外的一堆东西。

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.io.IOException;

public class Test {

    static class A {
        String aProp = "aProp";

        public String getAProp() {
            return aProp;
        }
    }

    static class B {
        String bProp = "bProp";
        A a = new A();

        @JsonProperty("bProp")
        public String getBProp() {
            return bProp;
        }

        @JsonSerialize(using = CustomSerializer.class)
        @JsonUnwrapped
        public A getA() {
            return a;
        }
    }


    static class CustomSerializer extends JsonSerializer<A> {
        @Override
        public boolean isUnwrappingSerializer() {
            return true;
        }

        @Override
        public void serialize(A a, JsonGenerator json, SerializerProvider provider) throws IOException {
            json.writeStringField("sProp1", "sProp1_" + a.getAProp());
            json.writeStringField("sProp2", "sProp2_" + a.getAProp());
        }
    }

    public static void main(String... a) throws Exception {
        final ObjectMapper o = new ObjectMapper();
        o.enable(SerializationFeature.INDENT_OUTPUT);
        System.out.println(o.writeValueAsString(new B()));
    }   
}

【讨论】:

  • 这正是我正在寻找的!做得好,谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-02-26
  • 2012-05-25
  • 2018-05-21
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多