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?
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:
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
.SCOPE_PROTOTYPE
car le REST DSL s'attend à obtenir une nouvelle instance de la DataFormat
. Voir CAMEL-7880 .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).
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
Où jacksonObjectMapper
est le nom du bean ObjectMapper créé par le Jackson2ObjectMapperBuilder configuré
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);
}
}
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
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();
}
}
}
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()));
}
}
Si Camel vous pose des problèmes, je reviendrais à utiliser directement des haricots:
Créez simplement un petit utilitaire Json capable de trier et de disséminer et d'autowire votre ObjectMapper préconfiguré.
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);