Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Quarkus Jackson ObjectMapper with JacksonDataFormat + Docs #3937

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/modules/ROOT/pages/reference/extensions/jackson.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,80 @@ Or add the coordinates to your existing project:
----

Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications.

== Usage

=== Configuring the Jackson `ObjectMapper`

There are a few ways of configuring the `ObjectMapper` that the `JacksonDataFormat` uses. These are outlined below.

==== `ObjectMapper` created internally by `JacksonDataFormat`

By default, `JacksonDataFormat` will create its own `ObjectMapper` and use the various configuration options on the `DataFormat`
to configure additional Jackson modules, pretty printing and other features.

==== Custom `ObjectMapper` for `JacksonDataFormat`

You can pass a custom `ObjectMapper` instance to `JacksonDataFormat` as follows.

[source,java]
----
ObjectMapper mapper = new ObjectMapper();
JacksonDataFormat dataFormat = new JacksonDataFormat();
dataFormat.setObjectMapper(mapper);
----

==== Using the Quarkus Jackson `ObjectMapper` with `JacksonDataFormat`

The Quarkus Jackson extension exposes an `ObjectMapper` CDI bean which can be discovered by the `JacksonDataFormat`.

[source,java]
----
ObjectMapper mapper = new ObjectMapper();
JacksonDataFormat dataFormat = new JacksonDataFormat();
// Make JacksonDataFormat discover the Quarkus Jackson `ObjectMapper` from the Camel registry
dataFormat.setAutoDiscoverObjectMapper(true);
----

If you are using the JSON binding mode in the Camel REST DSL and want to use the Quarkus Jackson `ObjectMapper`, it can be achieved as follows.

[source,java]
----
@ApplicationScoped
public class Routes extends RouteBuilder() {
public void configure() {
restConfiguration().dataFormatProperty("autoDiscoverObjectMapper", "true");
// REST definition follows...
}
}
----

You can perform customizations on the Quarkus `ObjectMapper` with a `ObjectMapperCustomizer`.

[source,java]
----
@Singleton
public class RegisterCustomModuleCustomizer implements ObjectMapperCustomizer {
public void customize(ObjectMapper mapper) {
mapper.registerModule(new CustomModule());
}
}
----

It's also possible to `@Inject` the Quarkus `ObjectMapper` and pass it to the `JacksonDataFormat`.

[source,java]
----
@ApplicationScoped
public class Routes extends RouteBuilder() {
@Inject
ObjectMapper mapper;

public void configure() {
JacksonDataFormat dataFormat = new JacksonDataFormat();
dataFormat.setObjectMapper(mapper);
// Routes definition follows...
}
}
----

73 changes: 73 additions & 0 deletions extensions/jackson/runtime/src/main/doc/usage.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
=== Configuring the Jackson `ObjectMapper`

There are a few ways of configuring the `ObjectMapper` that the `JacksonDataFormat` uses. These are outlined below.

==== `ObjectMapper` created internally by `JacksonDataFormat`

By default, `JacksonDataFormat` will create its own `ObjectMapper` and use the various configuration options on the `DataFormat`
to configure additional Jackson modules, pretty printing and other features.

==== Custom `ObjectMapper` for `JacksonDataFormat`

You can pass a custom `ObjectMapper` instance to `JacksonDataFormat` as follows.

[source,java]
----
ObjectMapper mapper = new ObjectMapper();
JacksonDataFormat dataFormat = new JacksonDataFormat();
dataFormat.setObjectMapper(mapper);
----

==== Using the Quarkus Jackson `ObjectMapper` with `JacksonDataFormat`

The Quarkus Jackson extension exposes an `ObjectMapper` CDI bean which can be discovered by the `JacksonDataFormat`.
zhfeng marked this conversation as resolved.
Show resolved Hide resolved

[source,java]
----
ObjectMapper mapper = new ObjectMapper();
JacksonDataFormat dataFormat = new JacksonDataFormat();
// Make JacksonDataFormat discover the Quarkus Jackson `ObjectMapper` from the Camel registry
dataFormat.setAutoDiscoverObjectMapper(true);
----

If you are using the JSON binding mode in the Camel REST DSL and want to use the Quarkus Jackson `ObjectMapper`, it can be achieved as follows.

[source,java]
----
@ApplicationScoped
public class Routes extends RouteBuilder() {
public void configure() {
restConfiguration().dataFormatProperty("autoDiscoverObjectMapper", "true");
// REST definition follows...
}
}
----

You can perform customizations on the Quarkus `ObjectMapper` with a `ObjectMapperCustomizer`.

[source,java]
----
@Singleton
public class RegisterCustomModuleCustomizer implements ObjectMapperCustomizer {
public void customize(ObjectMapper mapper) {
mapper.registerModule(new CustomModule());
}
}
----

It's also possible to `@Inject` the Quarkus `ObjectMapper` and pass it to the `JacksonDataFormat`.

[source,java]
----
@ApplicationScoped
public class Routes extends RouteBuilder() {
@Inject
ObjectMapper mapper;

public void configure() {
JacksonDataFormat dataFormat = new JacksonDataFormat();
dataFormat.setObjectMapper(mapper);
// Routes definition follows...
}
}
----
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.quarkus.component.dataformats.json.model.DummyObject;
import org.apache.camel.quarkus.component.dataformats.json.model.Order;
import org.apache.camel.quarkus.component.dataformats.json.model.Person;
import org.apache.camel.quarkus.component.dataformats.json.model.Pojo;
import org.apache.camel.quarkus.component.dataformats.json.model.TestJAXBPojo;
import org.apache.camel.quarkus.component.dataformats.json.model.TestOtherPojo;
Expand Down Expand Up @@ -249,17 +250,16 @@ public void jacksonMarshalGeneral() throws Exception {
@Path("jackson/object-mapper")
@GET
public void jacksonObjectMapper() throws Exception {
Map<String, Object> in = new HashMap<>();
in.put("name", "Camel");
Person person = new Person("John", "Doe", 44);

MockEndpoint mock = context.getEndpoint("mock:jackson-objectmapper-reverse", MockEndpoint.class);
mock.expectedMessageCount(1);
mock.message(0).body().isInstanceOf(Map.class);
mock.message(0).body().isEqualTo(in);
mock.message(0).body().isInstanceOf(Person.class);
mock.message(0).body().isEqualTo(person);

Object marshalled = producerTemplate.requestBody("direct:jackson-objectmapper-in", in);
Object marshalled = producerTemplate.requestBody("direct:jackson-objectmapper-in", person);
String marshalledAsString = context.getTypeConverter().convertTo(String.class, marshalled);
assertEquals("{\"name\":\"Camel\"}", marshalledAsString);
assertEquals("{\"first_name\":\"John\",\"last_name\":\"Doe\",\"age\":44}", marshalledAsString);

producerTemplate.sendBody("direct:jackson-objectmapper-back", marshalled);

Expand Down Expand Up @@ -495,7 +495,7 @@ public void jacksonConversionPojo(String body) throws Exception {
order.setPartName("Camel");

String json = (String) producerTemplate.requestBody("direct:jackson-conversion-pojo-test", order);
assertEquals("{\"id\":0,\"partName\":\"Camel\",\"amount\":1,\"customer_name\":\"Acme\"}", json);
assertEquals("{\"id\":0,\"part_name\":\"Camel\",\"amount\":1,\"customer_name\":\"Acme\"}", json);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.camel.model.dataformat.JsonLibrary;
import org.apache.camel.quarkus.component.dataformats.json.model.DummyObject;
import org.apache.camel.quarkus.component.dataformats.json.model.MyModule;
import org.apache.camel.quarkus.component.dataformats.json.model.Person;
import org.apache.camel.quarkus.component.dataformats.json.model.Pojo;
import org.apache.camel.quarkus.component.dataformats.json.model.TestJAXBPojo;
import org.apache.camel.quarkus.component.dataformats.json.model.TestPojo;
Expand Down Expand Up @@ -78,8 +79,7 @@ public void configure() throws Exception {
from("direct:jackson-marshal-inPojo").marshal(formatPojo);
from("direct:jackson-marshal-backPojo").unmarshal(formatPojo).to("mock:jackson-marshal-reversePojo");

this.getContext().getRegistry().bind("myMapper", new ObjectMapper());
JacksonDataFormat objectMapperFormat = new JacksonDataFormat();
JacksonDataFormat objectMapperFormat = new JacksonDataFormat(Person.class);
objectMapperFormat.setAutoDiscoverObjectMapper(true);
from("direct:jackson-objectmapper-in").marshal(objectMapperFormat);
from("direct:jackson-objectmapper-back").unmarshal(objectMapperFormat).to("mock:jackson-objectmapper-reverse");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.quarkus.component.dataformats.jackson.json;

import javax.inject.Singleton;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import io.quarkus.jackson.ObjectMapperCustomizer;

@Singleton
public class NamingStrategyCustomizer implements ObjectMapperCustomizer {
public void customize(ObjectMapper mapper) {
// Use snake case property names, so that we can verify the Quarkus ObjectMapper really is being used
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.quarkus.component.dataformats.json.model;

import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;

@RegisterForReflection(fields = false)
public class Person {

@JsonProperty
private String firstName;
@JsonProperty
private String lastName;
@JsonProperty
private int age;

public Person() {
}

public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
return age == person.age && Objects.equals(firstName, person.firstName) && Objects.equals(lastName, person.lastName);
}

@Override
public int hashCode() {
return Objects.hash(firstName, lastName, age);
}
}