Exemplos de anotações de Jackson
1. Visão geral
Neste artigo, vamos mergulhar profundamente emJackson Annotations.
Veremos como usar as anotações existentes, como criar anotações personalizadas e, finalmente, como desativá-las.
Leitura adicional:
Mais anotações de Jackson
Este artigo aborda algumas anotações de processamento JSON menos conhecidas fornecidas por Jackson.
Jackson - Relacionamentos bidirecionais
Como usar Jackson para quebrar o problema da recursão infinita em relacionamentos bidirecionais.
Introdução à desserialização personalizada em Jackson
Use Jackson para mapear JSON personalizado para qualquer gráfico de entidade java com controle total sobre o processo de desserialização.
2. Jackson Serialization Annotations
Primeiro, vamos dar uma olhada nas anotações de serialização.
2.1. @JsonAnyGetter
A anotação@JsonAnyGetter permite a flexibilidade de usar um campoMap como propriedades padrão.
Aqui está um exemplo rápido - a entidadeExtendableBean tem a propriedadename e um conjunto de atributos extensíveis na forma de pares chave / valor:
public class ExtendableBean {
public String name;
private Map properties;
@JsonAnyGetter
public Map getProperties() {
return properties;
}
}
Quando serializamos uma instância dessa entidade, obtemos todos os valores-chave emMap como propriedades simples e padrão:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
E aqui como a serialização dessa entidade se parece na prática:
@Test
public void whenSerializingUsingJsonAnyGetter_thenCorrect()
throws JsonProcessingException {
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("attr1"));
assertThat(result, containsString("val1"));
}
Também podemos usar o argumento opcionalenabled comofalse para desativar@JsonAnyGetter(). Nesse caso, oMap será convertido como JSON e aparecerá na variávelproperties após serialização.
2.2. @JsonGetter
A anotação@JsonGetter é uma alternativa à anotação@JsonProperty para marcar um método como um método getter.
No exemplo a seguir - especificamos o métodogetTheName() como o método getter da propriedadename da entidadeMyBean:
public class MyBean {
public int id;
private String name;
@JsonGetter("name")
public String getTheName() {
return name;
}
}
E aqui está como isso funciona na prática:
@Test
public void whenSerializingUsingJsonGetter_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
2.3. @JsonPropertyOrder
Podemos usar a anotação@JsonPropertyOrder para especificarthe order of properties on serialization.
Vamos definir uma ordem personalizada para as propriedades de uma entidadeMyBean:
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
E aqui está a saída da serialização:
{
"name":"My bean",
"id":1
}
E um teste simples:
@Test
public void whenSerializingUsingJsonPropertyOrder_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
Também podemos usar@JsonPropertyOrder(alphabetic=true) para ordenar as propriedades em ordem alfabética. E nesse caso, a saída da serialização será:
{
"id":1,
"name":"My bean"
}
2.4. @JsonRawValue
A anotação@JsonRawValue podeinstruct Jackson to serialize a property exactly as is.
No exemplo a seguir, usamos@JsonRawValue para incorporar algum JSON personalizado como um valor de uma entidade:
public class RawBean {
public String name;
@JsonRawValue
public String json;
}
A saída de serializar a entidade é:
{
"name":"My bean",
"json":{
"attr":false
}
}
E um teste simples:
@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
throws JsonProcessingException {
RawBean bean = new RawBean("My bean", "{\"attr\":false}");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("{\"attr\":false}"));
}
Também podemos usar o argumento booleano opcionalvalue que define se esta anotação está ativa ou não.
2.5. @JsonValue
@JsonValue indica um único método que a biblioteca usará para serializar a instância inteira.
Por exemplo, em um enum, anotamosgetName com@JsonValue para que qualquer entidade seja serializada por meio de seu nome:
public enum TypeEnumWithValue {
TYPE1(1, "Type A"), TYPE2(2, "Type 2");
private Integer id;
private String name;
// standard constructors
@JsonValue
public String getName() {
return name;
}
}
Nosso teste:
@Test
public void whenSerializingUsingJsonValue_thenCorrect()
throws JsonParseException, IOException {
String enumAsString = new ObjectMapper()
.writeValueAsString(TypeEnumWithValue.TYPE1);
assertThat(enumAsString, is(""Type A""));
}
2.6. @JsonRootName
A anotação@JsonRootName é usada - se o wrapper estiver habilitado - para especificar o nome do wrapper raiz a ser usado.
Empacotamento significa que, em vez de serializar aUser para algo como:
{
"id": 1,
"name": "John"
}
Vai ser embrulhado assim:
{
"User": {
"id": 1,
"name": "John"
}
}
Então, vamos ver um exemplo -we’re going to use the @JsonRootName annotation to indicate the name of this potential wrapper entity:
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
Por padrão, o nome do wrapper seria o nome da classe -UserWithRoot. Usando a anotação, obtemos a aparência mais limpauser:
@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new User(1, "John");
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = mapper.writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, containsString("user"));
}
Aqui está a saída da serialização:
{
"user":{
"id":1,
"name":"John"
}
}
Desde Jackson 2.4, um novo argumento opcionalnamespace está disponível para uso com formatos de dados como XML. Se o adicionarmos, ele se tornará parte do nome completo:
@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
public int id;
public String name;
// ...
}
Se o serializarmos comXmlMapper, a saída será:
1
John
2.7. @JsonSerialize
@JsonSerialize indicates a custom serializer to use when empacotandothe entity.
Vejamos um exemplo rápido. Vamos usar@JsonSerialize para serializar a propriedadeeventDate com umCustomDateSerializer:
public class EventWithSerializer {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
Aqui está o serializador Jackson personalizado simples:
public class CustomDateSerializer extends StdSerializer {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class t) {
super(t);
}
@Override
public void serialize(
Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
Vamos usá-los em um teste:
@Test
public void whenSerializingUsingJsonSerialize_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithSerializer event = new EventWithSerializer("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}
3. Anotações de desserialização de Jackson
A seguir - vamos explorar as anotações de desserialização de Jackson.
3.1. @JsonCreator
Podemos usar a anotação@JsonCreator para ajustar o construtor / fábrica usado na desserialização.
É muito útil quando precisamos desserializar algum JSON que não corresponda exatamente à entidade de destino que precisamos obter.
Vejamos um exemplo; digamos que precisamos desserializar o seguinte JSON:
{
"id":1,
"theName":"My bean"
}
No entanto, não há campotheName em nossa entidade de destino - há apenas um camponame. Agora, não queremos alterar a própria entidade - precisamos apenas de um pouco mais de controle sobre o processo de desempacotamento - anotando o construtor com@JsonCreatore usando a anotação@JsonProperty também:
public class BeanWithCreator {
public int id;
public String name;
@JsonCreator
public BeanWithCreator(
@JsonProperty("id") int id,
@JsonProperty("theName") String name) {
this.id = id;
this.name = name;
}
}
Vamos ver isso em ação:
@Test
public void whenDeserializingUsingJsonCreator_thenCorrect()
throws IOException {
String json = "{\"id\":1,\"theName\":\"My bean\"}";
BeanWithCreator bean = new ObjectMapper()
.readerFor(BeanWithCreator.class)
.readValue(json);
assertEquals("My bean", bean.name);
}
3.2. @JacksonInject
@JacksonInject indica que uma propriedade obterá seu valor da injeção e não dos dados JSON.
No exemplo a seguir - usamos@JacksonInject para injetar a propriedadeid:
public class BeanWithInject {
@JacksonInject
public int id;
public String name;
}
E é assim que funciona:
@Test
public void whenDeserializingUsingJsonInject_thenCorrect()
throws IOException {
String json = "{\"name\":\"My bean\"}";
InjectableValues inject = new InjectableValues.Std()
.addValue(int.class, 1);
BeanWithInject bean = new ObjectMapper().reader(inject)
.forType(BeanWithInject.class)
.readValue(json);
assertEquals("My bean", bean.name);
assertEquals(1, bean.id);
}
3.3. @JsonAnySetter
@JsonAnySetter nos permite a flexibilidade de usarMap como propriedades padrão. Na desserialização, as propriedades do JSON serão simplesmente adicionadas ao mapa.
Vamos ver como isso funciona - usaremos@JsonAnySetter para desserializar a entidadeExtendableBean:
public class ExtendableBean {
public String name;
private Map properties;
@JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
}
Este é o JSON que precisamos desserializar:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
E aqui está como tudo se conecta:
@Test
public void whenDeserializingUsingJsonAnySetter_thenCorrect()
throws IOException {
String json
= "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
ExtendableBean bean = new ObjectMapper()
.readerFor(ExtendableBean.class)
.readValue(json);
assertEquals("My bean", bean.name);
assertEquals("val2", bean.getProperties().get("attr2"));
}
3.4. @JsonSetter
@JsonSetter é uma alternativa a@JsonProperty - que marca o método como um método setter.
Isso é muito útil quando precisamos ler alguns dados JSON, excetothe target entity class doesn’t exactly match that data, e então precisamos ajustar o processo para torná-lo adequado.
No exemplo a seguir, especificaremos o método setTheName() como o configurador da propriedadename em nossa entidadeMyBean:
public class MyBean {
public int id;
private String name;
@JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}
Agora, quando precisamos desselecionar alguns dados JSON - isso funciona perfeitamente:
@Test
public void whenDeserializingUsingJsonSetter_thenCorrect()
throws IOException {
String json = "{\"id\":1,\"name\":\"My bean\"}";
MyBean bean = new ObjectMapper()
.readerFor(MyBean.class)
.readValue(json);
assertEquals("My bean", bean.getTheName());
}
3.5. @JsonDeserialize
@JsonDeserialize indica o uso de um desserializador personalizado.
Vamos ver como isso funciona - usaremos@JsonDeserialize para desserializar a propriedadeeventDate comCustomDateDeserializer:
public class EventWithSerializer {
public String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}
Este é o desserializador personalizado:
public class CustomDateDeserializer
extends StdDeserializer {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class> vc) {
super(vc);
}
@Override
public Date deserialize(
JsonParser jsonparser, DeserializationContext context)
throws IOException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
E aqui está o teste consecutivo:
@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()
throws IOException {
String json
= "{"name":"party","eventDate":"20-12-2014 02:30:00"}";
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
EventWithSerializer event = new ObjectMapper()
.readerFor(EventWithSerializer.class)
.readValue(json);
assertEquals(
"20-12-2014 02:30:00", df.format(event.eventDate));
}
3.6 @JsonAlias
O@JsonAlias defineone or more alternative names for a property during deserialization.
Vejamos como essa anotação funciona com um exemplo rápido:
public class AliasBean {
@JsonAlias({ "fName", "f_name" })
private String firstName;
private String lastName;
}
Aqui, temos um POJO e queremos desserializar JSON com valores comofName,f_name efirstName na variávelfirstName do POJO.
E aqui está um teste para garantir que esta anotação funcione conforme o esperado:
@Test
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
assertEquals("John", aliasBean.getFirstName());
}
4. Jackson Property Inclusion Annotations
4.1. @JsonIgnoreProperties
@JsonIgnoreProperties é uma anotação em nível de classe que marca uma propriedade ou uma lista de propriedades que Jackson irá ignorar.
Vejamos um exemplo rápido ignorando a propriedadeid da serialização:
@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
public int id;
public String name;
}
E aqui está o teste para garantir que o ignorar aconteça
@Test
public void whenSerializingUsingJsonIgnoreProperties_thenCorrect()
throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
Para ignorar quaisquer propriedades desconhecidas na entrada JSON sem exceção, podemos definirignoreUnknown=true de@JsonIgnoreProperties annotation.
4.2. @JsonIgnore
A anotação@JsonIgnore é usada para marcar uma propriedade a ser ignorada no nível do campo.
Vamos usar@JsonIgnore para ignorar a propriedadeid da serialização:
public class BeanWithIgnore {
@JsonIgnore
public int id;
public String name;
}
E o teste certificando-se de queid foi ignorado com sucesso:
@Test
public void whenSerializingUsingJsonIgnore_thenCorrect()
throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
4.3. @JsonIgnoreType
@JsonIgnoreType marca todas as propriedades de um tipo anotado para serem ignoradas.
Vamos usar a anotação para marcar todas as propriedades do tipoName a serem ignoradas:
public class User {
public int id;
public Name name;
@JsonIgnoreType
public static class Name {
public String firstName;
public String lastName;
}
}
Este é o teste simples para garantir que ignorar funcione corretamente:
@Test
public void whenSerializingUsingJsonIgnoreType_thenCorrect()
throws JsonProcessingException, ParseException {
User.Name name = new User.Name("John", "Doe");
User user = new User(1, name);
String result = new ObjectMapper()
.writeValueAsString(user);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
assertThat(result, not(containsString("John")));
}
4.4. @JsonInclude
Podemos usar@JsonInclude para excluir propriedades com valores vazios / nulos / padrão.
Vejamos um exemplo - excluindo nulos da serialização:
@JsonInclude(Include.NON_NULL)
public class MyBean {
public int id;
public String name;
}
Aqui está o teste completo:
public void whenSerializingUsingJsonInclude_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, null);
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
}
4.5. @JsonAutoDetect
@JsonAutoDetect pode substituir a semântica padrão dewhich properties are visible and which are not.
Vamos dar uma olhada em como a anotação pode ser muito útil com um exemplo simples - vamos habilitar a serialização de propriedades privadas:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
private int id;
private String name;
}
E o teste:
@Test
public void whenSerializingUsingJsonAutoDetect_thenCorrect()
throws JsonProcessingException {
PrivateBean bean = new PrivateBean(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("My bean"));
}
5. Anotações de manipulação de tipo polimórfico de Jackson
A seguir - vamos dar uma olhada nas anotações de manipulação de tipo polimórfico de Jackson:
-
@JsonTypeInfo - indica detalhes de que tipo de informação incluir na serialização
-
@JsonSubTypes - indica subtipos do tipo anotado
-
@JsonTypeName - define um nome de tipo lógico a ser usado para a classe anotada
Vejamos um exemplo mais complexo e use todos os três -@JsonTypeInfo,@JsonSubTypes,e@JsonTypeName – para serializar / desserializar a entidadeZoo:
public class Zoo {
public Animal animal;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public static class Animal {
public String name;
}
@JsonTypeName("dog")
public static class Dog extends Animal {
public double barkVolume;
}
@JsonTypeName("cat")
public static class Cat extends Animal {
boolean likesCream;
public int lives;
}
}
Quando fazemos serialização:
@Test
public void whenSerializingPolymorphic_thenCorrect()
throws JsonProcessingException {
Zoo.Dog dog = new Zoo.Dog("lacy");
Zoo zoo = new Zoo(dog);
String result = new ObjectMapper()
.writeValueAsString(zoo);
assertThat(result, containsString("type"));
assertThat(result, containsString("dog"));
}
Aqui está o que serializar a instânciaZoo comDog resultará em:
{
"animal": {
"type": "dog",
"name": "lacy",
"barkVolume": 0
}
}
Agora, para a desserialização - vamos começar com a seguinte entrada JSON:
{
"animal":{
"name":"lacy",
"type":"cat"
}
}
E vamos ver como isso é desempacotado para uma instânciaZoo:
@Test
public void whenDeserializingPolymorphic_thenCorrect()
throws IOException {
String json = "{\"animal\":{\"name\":\"lacy\",\"type\":\"cat\"}}";
Zoo zoo = new ObjectMapper()
.readerFor(Zoo.class)
.readValue(json);
assertEquals("lacy", zoo.animal.name);
assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}
6. Jackson General Annotations
A seguir - vamos discutir algumas das anotações mais gerais de Jackson.
6.1. @JsonProperty
Podemos adicionarthe @JsonProperty annotation to indicate the property name in JSON.
Vamos usar@JsonProperty para serializar / desserializar a propriedadename quando estivermos lidando com getters e setters não padrão:
public class MyBean {
public int id;
private String name;
@JsonProperty("name")
public void setTheName(String name) {
this.name = name;
}
@JsonProperty("name")
public String getTheName() {
return name;
}
}
Nosso teste:
@Test
public void whenUsingJsonProperty_thenCorrect()
throws IOException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
MyBean resultBean = new ObjectMapper()
.readerFor(MyBean.class)
.readValue(result);
assertEquals("My bean", resultBean.getTheName());
}
6.2. @JsonFormat
A anotação@JsonFormat especifica um formato ao serializar valores de data / hora.
No exemplo a seguir - usamos@JsonFormat para controlar o formato da propriedadeeventDate:
public class EventWithFormat {
public String name;
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}
E aqui está o teste:
@Test
public void whenSerializingUsingJsonFormat_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithFormat event = new EventWithFormat("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}
6.3. @JsonUnwrapped
@JsonUnwrapped define valores que devem ser desembrulhados / nivelados quando serializados / desserializados.
Vamos ver exatamente como isso funciona; usaremos a anotação para desembrulhar a propriedadename:
public class UnwrappedUser {
public int id;
@JsonUnwrapped
public Name name;
public static class Name {
public String firstName;
public String lastName;
}
}
Agora vamos serializar uma instância desta classe:
@Test
public void whenSerializingUsingJsonUnwrapped_thenCorrect()
throws JsonProcessingException, ParseException {
UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
UnwrappedUser user = new UnwrappedUser(1, name);
String result = new ObjectMapper().writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, not(containsString("name")));
}
Esta é a aparência da saída - os campos da classe aninhada estática desembrulhada junto com o outro campo:
{
"id":1,
"firstName":"John",
"lastName":"Doe"
}
6.4. @JsonView
@JsonView indica a visualização na qual a propriedade será incluída para serialização / desserialização.
Um exemplo mostrará exatamente como isso funciona - usaremos@JsonView para serializar uma instância da entidadeItem.
Vamos começar com as visualizações:
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
E agora aqui está a entidadeItem, usando as visualizações:
public class Item {
@JsonView(Views.Public.class)
public int id;
@JsonView(Views.Public.class)
public String itemName;
@JsonView(Views.Internal.class)
public String ownerName;
}
Finalmente - o teste completo:
@Test
public void whenSerializingUsingJsonView_thenCorrect()
throws JsonProcessingException {
Item item = new Item(2, "book", "John");
String result = new ObjectMapper()
.writerWithView(Views.Public.class)
.writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("2"));
assertThat(result, not(containsString("John")));
}
6.5. @JsonManagedReference, @JsonBackReference
The @JsonManagedReference and @JsonBackReference annotations can handle parent/child relationshipse contornar loops.
No exemplo a seguir - usamos@JsonManagedReferencee@JsonBackReference para serializar nossa entidadeItemWithRef:
public class ItemWithRef {
public int id;
public String itemName;
@JsonManagedReference
public UserWithRef owner;
}
Nossa entidadeUserWithRef:
public class UserWithRef {
public int id;
public String name;
@JsonBackReference
public List userItems;
}
E o teste:
@Test
public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect()
throws JsonProcessingException {
UserWithRef user = new UserWithRef(1, "John");
ItemWithRef item = new ItemWithRef(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("John"));
assertThat(result, not(containsString("userItems")));
}
6.6. @JsonIdentityInfo
@JsonIdentityInfo indica que a identidade do objeto deve ser usada ao serializar / desserializar valores - por exemplo, para lidar com tipos de problemas de recursão infinita.
No exemplo a seguir - temos uma entidadeItemWithIdentity com relação bidirecionala com a entidadeUserWithIdentity:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ItemWithIdentity {
public int id;
public String itemName;
public UserWithIdentity owner;
}
E a entidadeUserWithIdentity:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class UserWithIdentity {
public int id;
public String name;
public List userItems;
}
Agora,let’s see how the infinite recursion problem is handled:
@Test
public void whenSerializingUsingJsonIdentityInfo_thenCorrect()
throws JsonProcessingException {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("John"));
assertThat(result, containsString("userItems"));
}
Aqui está o resultado completo do item serializado e do usuário:
{
"id": 2,
"itemName": "book",
"owner": {
"id": 1,
"name": "John",
"userItems": [
2
]
}
}
6.7. @JsonFilter
A anotação@JsonFilter especifica um filtro a ser usado durante a serialização.
Vamos dar uma olhada em um exemplo; primeiro, definimos a entidade e apontamos para o filtro:
@JsonFilter("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}
Agora, no teste completo, definimos o filtro - que exclui todas as outras propriedades, excetoname da serialização:
@Test
public void whenSerializingUsingJsonFilter_thenCorrect()
throws JsonProcessingException {
BeanWithFilter bean = new BeanWithFilter(1, "My bean");
FilterProvider filters
= new SimpleFilterProvider().addFilter(
"myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String result = new ObjectMapper()
.writer(filters)
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
7. Anotação Jackson Personalizada
A seguir, vamos ver como criar uma anotação Jackson personalizada. We can make use of the @JacksonAnnotationsInside annotation:
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id", "dateCreated" })
public @interface CustomAnnotation {}
Agora, se usarmos a nova anotação em uma entidade:
@CustomAnnotation
public class BeanWithCustomAnnotation {
public int id;
public String name;
public Date dateCreated;
}
Podemos ver como ele combina as anotações existentes em uma mais simples e personalizada que podemos usar como uma abreviação:
@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
throws JsonProcessingException {
BeanWithCustomAnnotation bean
= new BeanWithCustomAnnotation(1, "My bean", null);
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
assertThat(result, not(containsString("dateCreated")));
}
A saída do processo de serialização:
{
"name":"My bean",
"id":1
}
8. Jackson MixIn Annotations
A seguir - vamos ver como usar as anotações Jackson MixIn.
Vamos usar as anotações MixIn para - por exemplo - ignorar propriedades do tipoUser:
public class Item {
public int id;
public String itemName;
public User owner;
}
@JsonIgnoreType
public class MyMixInForIgnoreType {}
Vamos ver isso em ação:
@Test
public void whenSerializingUsingMixInAnnotation_thenCorrect()
throws JsonProcessingException {
Item item = new Item(1, "book", null);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("owner"));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(User.class, MyMixInForIgnoreType.class);
result = mapper.writeValueAsString(item);
assertThat(result, not(containsString("owner")));
}
9. Desativar anotação de Jackson
Finalmente - vamos ver como podemosdisable all Jackson annotations. Podemos fazer isso desativandoMapperFeature.USE_ANNOTATIONS como no exemplo a seguir:
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
Agora, depois de desativar as anotações, elas não terão efeito e os padrões da biblioteca deverão ser aplicados:
@Test
public void whenDisablingAllAnnotations_thenAllDisabled()
throws IOException {
MyBean bean = new MyBean(1, null);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_ANNOTATIONS);
String result = mapper.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("name"));
}
O resultado da serialização antes de desativar as anotações:
{"id":1}
O resultado da serialização após desativar as anotações:
{
"id":1,
"name":null
}
10. Conclusão
Este tutorial foi aprofundado nas anotações de Jackson, apenas arranhando a superfície do tipo de flexibilidade que você pode obter usando-as corretamente.
A implementação de todos esses exemplos e trechos de código pode ser encontrada emGitHub project - este é um projeto baseado em Maven, portanto, deve ser fácil de importar e executar como está.