web-dev-qa-db-fra.com

Comment configurer Jackson ObjectMapper pour Camel dans Spring Boot

J'essaie de sérialiser et de désérialiser les POJO vers et depuis JSON sur les itinéraires Camel à l'aide de Jackson. Certains d'entre eux ont des champs Java 8 LocalDate, et je veux qu'ils soient sérialisés en tant que chaîne AAAA-MM-JJ, et non en tant que tableau d'entiers.

Nous utilisons uniquement la configuration Java pour notre application Spring Boot, donc pas de configuration XML Camel.

J'ai créé avec succès un ObjectMapper qui fait ce que je veux, qui est utilisé par d'autres parties de notre système en l'ajoutant à nos dépendances:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

et ceci à notre configuration d'application:

@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .build();
}

Exemple de route REST sortante:

@Component
public class MyRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        restConfiguration().component("servlet").contextPath("/mycontext")
                .port(8080).bindingMode(RestBindingMode.json);

        rest("/myendpoint)
                .get()
                .route()
                .to("bean:myService?method=myMethod()");
    }
}

Exemple d'itinéraire de message entrant:

@Component
public class MyRouteBuilder extends RouteBuilder {

    @Autowired
    private MyBean myBean;

    @Override
    public void configure() {
        from(uri)
                .unmarshal().json(JsonLibrary.Jackson)
                .bean(myBean);
    }
}

Toutefois, par défaut, Camel crée ses propres instances ObjectMapper. Par conséquent, il ne détecte ni les sérialiseurs/désérialiseurs JSR310 que Jackson2ObjectMapperBuilder ajoute automatiquement, ni la fonctionnalité Désactivé WRITE_DATES_AS_TIMESTAMPS. J'ai lu la documentation Camel JSON , mais elle ne montre pas comment ajouter un DataFormat personnalisé à l'aide de la configuration Spring, ni comment appliquer une personnalisation globale à tous les types.

Alors, comment puis-je dire à Camel d'utiliser mon ObjectMapper, en utilisant uniquement la configuration Java de Spring Boot?

13
David Edwards

J'ai trouvé une solution en parcourant le code Camel. Par conséquent, même s’il fait ce que je veux, il risque de ne pas fonctionner avec les futures versions de Camel, car il semble être non documenté et potentiellement non pris en charge.

Tout ce que je fais est d'ajouter le haricot suivant à ma config de printemps, en plus de mon haricot ObjectMapper dans la question:

@Bean(name = "json-jackson")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JacksonDataFormat jacksonDataFormat(ObjectMapper objectMapper) {
    return new JacksonDataFormat(objectMapper, HashMap.class);
}

Les points cruciaux à noter:

  • Il n'y a pas de constructeur pour JacksonDataFormat qui prend une ObjectMapper sans type unmarshal. Cependant, dans le constructeur par défaut, un HashMap.class est utilisé lorsqu'aucun type unmarshal n'est fourni, donc je l'utilise. Par une certaine magie, cela semble s’habituer ensuite à annuler la fusion de tous les types de POJO. Si vous avez également besoin de formats de données plus spécifiques pour d'autres classes, vous devez également définir la variable ObjectMapper.
  • Camel semble chercher dans le registre des haricots un "haricot appelé" json-jackson ". Par conséquent, si vous configurez le haricot Spring pour qu'il utilise ce nom, Camel trompe Camel pour qu'il n'en crée pas un nouveau et utilise le mien à la place.
  • La portée du bean doit être définie sur SCOPE_PROTOTYPE car le REST DSL s'attend à obtenir une nouvelle instance de la DataFormat. Voir CAMEL-7880 .
6
David Edwards

Créez la JacksonDataFormat en code Java et activez/désactivez les fonctionnalités souhaitées, puis utilisez cette instance dans la route Camel.

 .unmarshal(myInstanceGoesHere).
3
Claus Ibsen

J'ai réussi à configurer ObjectMapper pour Camel assez facilement en utilisant org.Apache.camel:camel-jackson-starter:2.20.0

Il expose certaines des propriétés utiles de ObjectMapper pour la configuration via les propriétés de l'application Spring. WRITE_DATES_AS_TIMESTAMPS, par exemple, peut être défini directement à partir du fichier application.yaml ou application.properties.

Recherchez la classe JacksonDataFormatConfiguration pour plus de détails.

J'avais aussi besoin d'utiliser des Mixins, donc j'avais encore besoin de configurer Camel pour utiliser un ObjectMapper de Spring. J'ai fini avec ceci:

Bean de configuration:

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) {
            builder.mixIn(Person.class, PersonMixin.class);
        }
    }
}

application.yaml:

camel:
  dataformat:
    json-jackson:
      disable-features: WRITE_DATES_AS_TIMESTAMPS
      object-mapper: jacksonObjectMapper

jacksonObjectMapper est le nom du bean ObjectMapper créé par le Jackson2ObjectMapperBuilder configuré

2
mtiitson

En utilisant Spring et Camel 2.18.1, j'ai pu obtenir le même résultat en ajoutant les dépendances suivantes:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.1</version>
</dependency>

et dans une classe CamelContextConfiguration, le câblage automatique de JacksonDataFormat afin de configurer la découverte des modules de chemin d'accès aux classes et la configuration des options de sérialisation:

@Configuration
public class CamelContextConfig implements CamelContextConfiguration {

    @Autowired
    public JacksonDataFormat jacksonDataFormat;

    @Override
    public void beforeApplicationStart(CamelContext camelContext) {
    }

    @Override
    public void afterApplicationStart(CamelContext camelContext) {
        jacksonDataFormat
            .getObjectMapper()
            .findAndRegisterModules()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}
2
Marco Santarelli

Si quelqu'un d'autre se demandait comment utiliser le correctif mis à jour. 2.17 .. Je l’ai obtenu avec cette configuration XML:

 <camel:camelContext id="defaultCamelContext">
       .....
        <camel:dataFormats>
            <camel:json id="json" library="Jackson"  objectMapper="myObjectMapper"/>
        </camel:dataFormats>

 </camel:camelContext>

..where myObjectMapper est le nom d'un bean de printemps de type ObjectMapper

1
Jacek Obarymski

Jusqu'à présent, seule la suggestion de @ david-edwards a fonctionné pour moi. J'ai d'abord défini un bean de format de données avec l'id: "json-jackson"

<bean id="json-jackson" class="com.mydomain.JacksonDataFormatExt" />

Puis la classe de format:

public class JacksonDataFormatExt extends JacksonDataFormat{

    public JacksonDataFormatExt(){
        super();
        setPrettyPrint(true);
        setEnableFeatures(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS.name());
        SimpleModule s = new SimpleModule();
        s.addSerializer(CustomEnum.class, new CustomEnumSerializer());
        addModule(s);
    }
}

Et la classe CustomEnumSerializer:

public class CustomEnumSerializer extends JsonSerializer<CustomEnum> {

    @Override
    public void serialize(CustomEnumvalue, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        String stringValue = value.getNlsText();
        if(stringValue != null && !stringValue.isEmpty() && !stringValue.equals("null")) {
            jgen.writeString(stringValue);
        } else {
            jgen.writeNull();
        }
    }
}
1
Georgios Stathis

Je n'ai pu obtenir aucun des exemples au travail. Un peu déçue que cela soit assez compliqué de lire des solutions de contournement. 

À mon avis, chamel devrait faciliter l’utilisation du mappeur d’objets par défaut Spring en utilisant le même bean de Jackson fourni avec l’application.

J'ai oublié l'utilisation de .json() et l'ai échangé contre un processeur.

comme ce qui suit, il utilisait l’objetMapper fourni par Spring.

Itinéraire

from(CONSUME_TAG)
 .process("jsonProcessor")
 .to("direct:anotherRoute")
 .end();

Processeur générique remarquez comment cela entraîne automatiquement le bean objectMapper de démarrage initial.

@Component
public class JsonProcessor implements Processor {

    @Autowired
    ObjectMapper objectMapper;

    @Override
    public void process(Exchange exchange) throws Exception {
        exchange.getOut().setBody(objectMapper.writeValueAsString(exchange.getIn().getBody()));
    }

}
0
Robbo_UK

Si Camel vous pose des problèmes, je reviendrais à utiliser directement des haricots:

  1. Créez simplement un petit utilitaire Json capable de trier et de disséminer et d'autowire votre ObjectMapper préconfiguré.

  2. Tirez parti de l'intégration géniale des haricots Spring de Camels pour appeler votre service public et transformer le message dans l'itinéraire, par exemple:

         from(uri)
            .unmarshal().json(JsonLibrary.Jackson)
            .beanRef("jsonUtil", "unmarshal")
            .bean(myBean);
    
0
Fritz Duchardt