使用Jackson和Spring Boot的条件JsonProperty

时间:2016-01-29 16:24:30

标签: java json spring spring-boot jackson

Spring Boot应用程序的任务是每隔几分钟更新一次远程集成API。此应用程序可以部署到测试或prod环境,应用程序将通过" application.properties"通知应该查看的终点。旗。正在使用Jackson序列化POJO并将其推送到端点,其中JsonProperty注释包含要推送到的API的字段ID。

@JsonProperty("field_001)
private String name;

@JsonProperty("field_002)
private String address;

这些值的字段标签在测试端点上有所不同。因此,测试端点可能希望属性映射为

@JsonProperty("field_005)
private String name;

@JsonProperty("field_006)
private String address;

我希望能够利用Spring Boot原生支持基于配置文件的属性文件。从运行时读取外部属性文件中的JsonProperty注释值。

例如,

可能有三个文件application.properties,application-test.properties和application-prod.properties。 除了基于" spring.profiles.active"的vanilla属性文件之外,Spring Boot还可以读入test或prod属性。设置。

...- test.properties将包含测试服务器字段的常量值。并且......- prod.properties将包含prod服务器字段的常量值。

嵌套注释,例如Spring的@Value标记,如下所示:

@JsonProperty(@Value("${property.file.reference.here})) 

似乎不起作用。

2 个答案:

答案 0 :(得分:1)

我怀疑你可以在Jackson注释中使用Spring Expression Language(SpEL)来做这件事,就像你正在尝试的那样(有或没有@Value注释)。

我会通过创建一个JsonSerializer<YourPojo>和/或JsonDeserializer<YourPojo>来接收您的SpEL表达式并使用提供的字段名称创建(或读取)。

//make me a spring managed bean!
public class PojoSerializer extends JsonSerializer<YourPojo> {
    @Value("${property.file.reference.name")
    private String nameField;

    @Value("${property.file.reference.address")
    private String addrField;

    @Override
    public void serialize(YourPojo pojo, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        jgen.writeStartObject();
        jgen.writeStringField(nameField, pojo.getName());
        jgen.writeStringField(addrField, pojo.getAddress());
        jgen.writeEndObject();
    }
}

由于这是一个Spring托管bean,您需要将其插入Spring托管ObjectMapper

ObjectMapper mapper = //my ObjectMapper from spring
PojoSerializer pojoSerializer = //my PojoSerializer from spring

SimpleModule module = new SimpleModule("MyModule", new Version(1, 0, 0, null));
module.addSerializer(YourPojo.class, pojoSerializer);
mapper.registerModule(module);

SpringBoot的AutoConfiguration可能不需要其中一些。我一般不知道SpringBoot会为其Jackson AutoConfiguration选择什么,但JsonSerializerJsonDeserializer可能会在ApplicationContext中自动注册。

答案 1 :(得分:1)

我为重提一个老问题深表歉意,但仍然找不到令人满意的答案。

这是我使用扩展JacksonAnnotationIntrospector的解决方案,它允许在${environment.properties}批注中使用@JsonProperty

首先扩展内省者

public class DynamicJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {
    private final Environment environment;

    public DynamicJacksonAnnotationIntrospector(Environment environment) {
        this.environment = environment;
    }

    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        PropertyName name = super.findNameForSerialization(a);
        if (name == null) {
            return null;
        }
        String simpleName = name.getSimpleName();
        return PropertyName.construct(environment.resolvePlaceholders(simpleName), name.getNamespace());
    }
    //For deserialization I think the same mechanism could be used,
    //just override `findNameForDeserialization`, although I haven't tested it
}

然后将其与ObjectMapper配置一起使用

@Configuration
public class ObjectMapperConfiguration {
    @Bean
    public ObjectMapper getObjectMapper(DynamicJacksonAnnotationIntrospector introspector) {
        ObjectMapper mapper = new ObjectMapper();
        SerializationConfig config = mapper.getSerializationConfig().withInsertedAnnotationIntrospector(introspector);
        mapper.setConfig(config);
        return mapper;
    }

    @Bean
    public DynamicJacksonAnnotationIntrospector introspector(Environment environment) {
        return new DynamicJacksonAnnotationIntrospector(environment);
    }
}

示例:

public class DynamicTestClass {
    @JsonProperty("${dynamic.property.name}")
    private String dynamicPropertyName;
    //getters/setters
}
@ContextConfiguration(classes = [
        ObjectMapperConfiguration
])
@TestPropertySource("classpath:test.properties")
class DynamicJacksonAnnotationIntrospectorTest extends Specification {
    @Autowired
    ObjectMapper mapper

    def "should find name for serialization from properties"() {
        def bean = new DynamicTestClass()
        bean.dynamicPropertyName = "qwerty"

        when:
        def result = mapper.writeValueAsString(bean)

        then:
        result == "{\"overriddenName\":\"qwerty\"}"
    }
}

test.properties

dynamic.property.name=overriddenName

该解决方案是向下兼容的,因此您仍可以在@JsonProperty中使用常量值

相关问题