Экземпляр Spring @ConfigurationProperties в JSON с помощью jackson: сериализатор не найден

#json #mongodb #spring-boot #java-8 #jackson

#json #mongodb #весенняя загрузка #java-8 #джексон

Вопрос:

У меня следующий код:

 @Data
@Validated
@ConfigurationProperties
public class Keys {

    private final Key key = new Key();

    @Data
    @Validated
    @ConfigurationProperties(prefix = "key")
    public class Key {

        private final Client        client          = new Client();
        private final IntentToken   intentToken     = new IntentToken();
        private final Intent        intent          = new Intent();
        private final OAuth         oauth           = new OAuth();
        private final ResourceToken resourceToken   = new ResourceToken();

        @Valid @NotNull private String authorization;
        @Valid @NotNull private String bearer;
    ...
    }
}
  

Это экземпляр, представляющий файл свойств, такой как:

 key.authorization=Authorization
key.bearer=Bearer
..
  

Поскольку у меня могут быть разные источники для свойств (файл свойств, MongoDB и т.д.), У меня есть клиент, который наследует ключи следующим образом:

Исходные файлы свойств

 @Component
@Configuration
@Primary
@PropertySource("classpath:${product}-keys.${env}.properties")
//@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class CustomerKeysProperties extends Keys {

}
  

Исходный код Mongo

 @Data
@EqualsAndHashCode(callSuper=true)
@Component
//@Primary
@Document(collection = "customerKeys")
public class CustomerKeysMongo extends Keys {
    @Id
    private String id;
}
  

Я просто выбираю источник, который хочу использовать для аннотирования класса @Primary . В приведенном выше примере CustomerKeysProperties является активным источником.

Все это работает нормально. Проблема, с которой я сталкиваюсь, заключается в том, что я пытаюсь преобразовать экземпляр CustomerKeysProperties в JSON, как в приведенном ниже коде:

 @SpringBootApplication
public class ConverterUtil {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(ConverterUtil.class, args);
    }

    @Component
    class CustomerInitializer implements CommandLineRunner {

        @Autowired 
        private Keys k;
        private final ObjectMapper mapper = new ObjectMapper();

        @Override
        public void run(String... args) throws Exception {
            mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
            //mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            String jsonInString = mapper.writeValueAsString(k);
            System.out.println(jsonInString);
        }
    }
}
  

While k contains all the properties set, the conversion fails:

 Caused by:     com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: x.client.customer.properties.CustomerKeysProperties$$EnhancerBySpringCGLIB$$eda308bd["CGLIB$CALLBACK_0"]->org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor["advised"]->org.springframework.aop.framework.ProxyFactory["targetSource"]->org.springframework.aop.target.SingletonTargetSource["target"]->x.client.customer.properties.CustomerKeysProperties$$EnhancerBySpringCGLIB$$4fd6c568["CGLIB$CALLBACK_0"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1191)
  

И если я раскомментирую
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
как указано в журналах, у меня в Jackson происходит бесконечный цикл, вызывающий stackoverflow:

 at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
..
  

Вопросы

В конце я просто хочу предоставить класс Util, который может преобразовать файл свойств в формат JSON, который будет сохранен в MongoDB.

  1. Как я могу решить эту проблему?
  2. Как я могу преобразовать файл свойств в JSON без прохождения через объект выше?
  3. Могу ли я сохранить произвольный Java-компонент в MongoDB с автоматическим преобразованием в JSON?

Ответ на любой из 3 вопросов выше был бы полезен.

Примечания

  • Следует отметить, что я использую lombok. Не уверен, что это проблема.
  • Другое предположение заключается в том, что я пытаюсь сериализовать компонент, управляемый Spring, и прокси, который он использует, приводит к тому, что Джексон не может выполнить сериализацию? Если да, то каким может быть поворот?

Спасибо!

Ответ №1:

Итак, найдена проблема:

джексон не может обработать управляемый компонент.

Поворот был

             try (InputStream input = getClass().getClassLoader().getResourceAsStream("foo.properties")) {
                JavaPropsMapper mapper = new JavaPropsMapper();
                Keys keys = mapper.readValue(input, Keys.class);
                ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
                String res = ow.writeValueAsString(keys);
                System.out.println(res);
            } catch (IOException e) {
                e.printStackTrace();
            }
  

где Keys был компонент, управляемый Spring, который я вводил.

И:

JavaPropsMapper исходят из:

     <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-properties</artifactId>
    </dependency>