Fastjson da Alibaba: Otimizando o Processamento de JSON em Aplicações Java

O formato JSON (JavaScript Object Notation) é uma escolha comum para a troca de dados em sistemas modernos. Sua estrutura leve e hierárquica facilita a leitura e escrita por humanos, ao mesmo tempo que permite que máquinas o analisem e gerem de forma eficiente, contribuindo para a otimização da comunicação em rede.

Em ambientes Java, onde a manipulação de dados ocorre predominantemente através de objetos, a conversão entre objetos Java e JSON é uma necessidade constante. Bibliotecas de serialização e desserialização de JSON preenchem essa lacuna, permitindo que objetos sejam transformados em representações JSON para transmissão e vice-versa, independentemente da linguagem de programação do consumidor.

Entre as diversas opções disponíveis, o Fastjson da Alibaba se destaca. Esta biblioteca de código aberto para Java oferece um conjunto robusto de ferramentas para converter objetos Java em cadeias de caracteres JSON e vice-versa. Fastjson é capaz de processar qualquer objeto Java, incluindo aqueles de bibliotecas de terceiros sem acesso ao código-fonte.

O que é Fastjson?

Fastjson é a biblioteca de serialização e desserialização de JSON da Alibaba. Ela converte dados em formato JSON para objetos Java (JavaBeans) e também permite a serialização de JavaBeans para representações JSON.

Benefícios do Fastjson

  • Performance Excepcional: Fastjson é reconhecido por sua velocidade superior em comparação com outras bibliotecas JSON para Java, mantendo sua liderança em desempenho desde seu lançamento em 2011.
  • Ampla Adoção: Utilizado extensivamente pela Alibaba em dezenas de milhares de servidores, Fastjson é uma solução amplamente aceita na indústria. Foi inclusive eleito um dos softwares de código aberto mais populares na China em 2012.
  • Rigorosamente Testado: A biblioteca possui uma vasta suíte de testes, com mais de 3321 casos na versão 1.2.11. Cada lançamento passa por testes de regressão completos para garantir a estabilidade e a qualidade.
  • Interface Simplificada: A API do Fastjson é intuitiva e fácil de usar.
  • Funcionalidades Completas: Suporte a tipos genéricos, processamento de streams para grandes volumes de texto, enumerações e extensões para serialização e desserialização.

Como Obter o Fastjson

Fastjson está disponível para download e inclusão em projetos Java:

As versões mais recentes do Fastjson são publicadas no repositório central Maven. Recomenda-se utilizar a versão mais atualizada.

<dependency>
     <groupId>com.alibaba>/groupId>
     <artifactId>fastjson</artifactId>
     <version>x.x.x</version>
</dependency>

Substitua x.x.x pelo número da versão desejada.

Principais APIs do Fastjson

A classe central para interagir com o Fastjson é com.alibaba.fastjson.JSON. Os métodos mais utilizados são JSON.toJSONString para serialização e JSON.parseObject para desserialização.

package com.alibaba.fastjson;

import java.io.Serializable; // Exemplo de interface, mantendo a abstração

public abstract class JSON implements Serializable { // Modificação sutil para a declaração

    // Converte um objeto Java em uma string JSON
    public static final String toJSONString(Object dataObject);

    // Converte uma string JSON em um objeto Java do tipo especificado
    public static final <T> T parseObject(String jsonText, Class<T> targetClass, Feature... parsingFeatures);
}

Exemplo de Serialização:

String serializedData = JSON.toJSONString(myJavaObject);

Exemplo de Desserialização:

MyValueObject resultObject = JSON.parseObject(jsonInputString, MyValueObject.class);

Desserialização com Tipos Genéricos:

import com.alibaba.fastjson.TypeReference;
import java.util.List;

List<MyValueObject> objectList = JSON.parseObject(jsonArrayString, new TypeReference<List<MyValueObject>>() {});

Desempenho do Fastjson

Fastjson é amplamente considerado a biblioteca JSON mais rápida para a linguagem Java, superando até mesmo alternativas como Jackson. Resultados de testes independentes estão disponíveis em: https://github.com/eishay/jvm-serializers/wiki.

Ao realizar seus próprios testes de dseempenho, é crucial desativar a detecção de referência circular para obter resultados precisos:

String jsonWithDisabledRefDetect = JSON.toJSONString(myObject, SerializerFeature.DisableCircularReferenceDetect);
MyObject deserializedObject = JSON.parseObject(jsonString, MyObject.class, Feature.DisableCircularReferenceDetect);

Adicionalmente, Fastjson demonstrou ser aproximadamente 6 vezes mais rápido que Gson em certos cenários. Abaixo, um fragmento de resultados de benchmarking para referência:

Checking correctness…
[done]
Pre-warmup… java-built-in hessian kryo protostuff-runtime avro-generic msgpack json/jackson/databind json/jackson/databind-strings json/jackson/db-afterburner json/google-gson/databind json/svenson-databind json/flexjson/databind json/fastjson/databind smile/jackson/databind smile/jackson/db-afterburner smile/protostuff-runtime bson/jackson/databind xml/xstream+c xml/jackson/databind-aalto
[done]
pre. create ser deser shal +deep total size +dfl
java-built-in 63 5523 27765 28084 28162 33686 889 514
hessian 64 3776 6459 6505 6690 10466 501 313
kryo 63 809 962 937 1001 1810 214 133
protostuff-runtime 62 671 903 920 957 1627 241 151
avro-generic 436 1234 1122 1416 1760 2994 221 133
msgpack 61 789 1369 1385 1449 2238 233 146
json/jackson/databind 60 1772 3089 3113 3246 5018 485 261
json/jackson/databind-strings 64 2346 3739 3791 3921 6267 485 261
json/jackson/db-afterburner 64 1482 2220 2233 2323 3805 485 261
json/google-gson/databind 64 7076 4894 4962 5000 12076 486 259
json/svenson-databind 64 5422 12387 12569 12468 17890 495 266
json/flexjson/databind 62 20923 26853 26873 27272 48195 503 273
json/fastjson/databind 63 1250 1208 1206 1247 2497 486 262
smile/jackson/databind 60 1697 2117 2290 2298 3996 338 241
smile/jackson/db-afterburner 60 1300 1614 1648 1703 3003 352 252
smile/protostuff-runtime 61 1275 1612 1638 1685 2961 335 235
bson/jackson/databind 63 5151 6729 6977 6918 12069 506 286
xml/xstreamc 62 6358 13208 13319 13516 19874 487 244
xml/jackson/databind-aalto 62 2955 5332 5465 5584 8539 683 286

Exemplo de Uso do Fastjson

Considere a modelagem de uma turma e seus respectivos alunos:

Objeto Turma (SchoolClass):

import java.util.ArrayList;
import java.util.List;

public class SchoolClass {
    private Long identifier;
    private String className;
    private List<StudentEntry> students = new ArrayList<>();

    // Construtores, getters e setters

    public Long getIdentifier() { return identifier; }
    public void setIdentifier(Long identifier) { this.identifier = identifier; }
    public String getClassName() { return className; }
    public void setClassName(String className) { this.className = className; }
    public List<StudentEntry> getStudents() { return students; }
    public void setStudents(List<StudentEntry> students) { this.students = students; }

    public void addStudent(StudentEntry student) {
        this.students.add(student);
    }

    @Override
    public String toString() {
        return "SchoolClass{" +
                "identifier=" + identifier +
                ", className='" + className + '\'' +
                ", students=" + students +
                '}';
    }
}

Objeto Aluno (StudentEntry):

public class StudentEntry {
    private Long   studentId;
    private String studentName;

    // Construtores, getters e setters

    public Long getStudentId() { return studentId; }
    public void setStudentId(Long studentId) { this.studentId = studentId; }
    public String getStudentName() { return studentName; }
    public void setStudentName(String studentName) { this.studentName = studentName; }

    @Override
    public String toString() {
        return "StudentEntry{" +
                "studentId=" + studentId +
                ", studentName='" + studentName + '\'' +
                '}';
    }
}

Função Principal (AppRunner):

import com.alibaba.fastjson.JSON;

public class AppRunner {
    public static void main(String[] args) {
        SchoolClass myClass = new SchoolClass();
        myClass.setIdentifier(101L);
        myClass.setClassName("Matemática Avançada");

        StudentEntry firstStudent = new StudentEntry();
        firstStudent.setStudentId(1L);
        firstStudent.setStudentName("Alice Silva");

        StudentEntry secondStudent = new StudentEntry();
        secondStudent.setStudentId(2L);
        secondStudent.setStudentName("Bruno Costa");

        myClass.addStudent(firstStudent);
        myClass.addStudent(secondStudent);

        // Serializa o objeto para JSON
        String jsonOutput = JSON.toJSONString(myClass);
        System.out.println("String JSON gerada: " + jsonOutput);

        // Desserializa a string JSON de volta para um objeto Java
        SchoolClass deserializedClass = JSON.parseObject(jsonOutput, SchoolClass.class);
        System.out.println("Objeto Java reconstruído: " + deserializedClass);
    }
}

Saída Esperada:

String JSON gerada: {"className":"Matemática Avançada","identifier":101,"students":[{"studentId":1,"studentName":"Alice Silva"},{"studentId":2,"studentName":"Bruno Costa"}]}
Objeto Java reconstruído: SchoolClass{identifier=101, className='Matemática Avançada', students=[StudentEntry{studentId=1, studentName='Alice Silva'}, StudentEntry{studentId=2, studentName='Bruno Costa'}]}

Gerenciamento de Valores Nulos no Fastjson

Por padrão, Fastjson omite valores nulos durante a serialização, seja em Maps ou em atributos de objetos, para reduzir o tamanho da representação JSON. Contudo, é possível configurar o Fastjson para incluir esses valores.

Para incluir explicitamente valores nulos, utilize a feature SerializerFeature.WriteMapNullValue:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

MyDataModel data = new MyDataModel(); // Supondo que 'data' tenha alguns campos nulos
String jsonWithNulls = JSON.toJSONString(data, SerializerFeature.WriteMapNullValue);

Fastjson oferece outras SerializerFeature para controle granular sobre a serialização de nulos em tipos específicos:

SerializerFeature Descrição
WriteNullListAsEmpty Serializa campos de coleção com valor nulo como uma lista vazia [].
WriteNullStringAsEmpty Serializa campos de string com valor nulo como uma string vazia "".
WriteNullNumberAsZero Serializa campos numéricos com valor nulo como 0.
WriteNullBooleanAsFalse Serializa campos booleanos com valor nulo como false.

É possível combinar várias features, como no exemplo abaixo:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.util.List;

class ProductInventory {
    public List<Object> productItems;
    public String  description;
    public Integer stockAmount;
    public Boolean available;
}

ProductInventory inventory = new ProductInventory(); // Assumindo alguns campos nulos

String outputJson = JSON.toJSONString(
    inventory,
    SerializerFeature.WriteMapNullValue,
    SerializerFeature.WriteNullListAsEmpty,
    SerializerFeature.WriteNullStringAsEmpty,
    SerializerFeature.WriteNullNumberAsZero,
    SerializerFeature.WriteNullBooleanAsFalse
);

Manipulação de Datas no Fastjson

Fastjson oferece métodos diretos para formatar datas durante a serialização.

Para formatar uma data específica:

import com.alibaba.fastjson.JSON;
import java.util.Date;

Date currentDate = new Date();
String formattedDate = JSON.toJSONStringWithDateFormat(currentDate, "yyyy-MM-dd HH:mm:ss.SSS");

Utilizando o formato de data ISO-8601:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

// Supondo um objeto 'event' com um campo Date
String isoFormattedJson = JSON.toJSONString(event, SerializerFeature.UseISO8601DateFormat);

Para uma alteração global do formato de data (afeta todos os objetos serializados, a menos que sobreposto):

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd"; // Define o formato padrão
// Supondo um objeto 'report' com campos Date
String customGlobalFormattedJson = JSON.toJSONString(report, SerializerFeature.WriteDateUseDateFormat);

Na desserialização, Fastjson é inteligente o suficiente para reconhecer e converter automaticamente uma variedade de formatos de data, incluindo:

  • Formato de data ISO-8601
  • yyyy-MM-dd
  • yyyy-MM-dd HH:mm:ss
  • yyyy-MM-dd HH:mm:ss.SSS
  • Números representando milissegundos
  • Strings de números representando milissegundos
  • Formato de data JSON do .NET
  • new Date(198293238)

Para cenários onde campos de data dentro de um mesmo objeto requerem formatos distintos, a serialização personalizada é a solução ideal.

Serialização Personalizada com Fastjson

Fastjson oferece várias abordagens para personalizar o processo de serialização:

  • Através da anotação @JSONField.
  • Através da anotação @JSONType.
  • Utilizando interfaces SerializeFilter.
  • Utilizando interfaces ParseProcess para desserialização personalizada.

Personalização com @JSONField

1. Visão Geral da Anotação @JSONField

package com.alibaba.fastjson.annotation;

public @interface JSONField {
    // Ordem de serialização e desserialização (disponível a partir da v1.1.42)
    int ordinal() default 0;

    // Nome do campo no JSON
    String name() default "";

    // Formato específico para campos de data/hora
    String format() default "";

    // Controla se o campo deve ser serializado
    boolean serialize() default true;

    // Controla se o campo deve ser desserializado
    boolean deserialize() default true;
}

2. Aplicação da Anotação @JSONField

A anotação @JSONField pode ser aplicada em campos (fields) ou em métodos getter/setter.

Aplicando em campos:

import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;

public class UserDetails {
     @JSONField(name="userID")
     private int identifier;

     @JSONField(name="birthDate", format="yyyy-MM-dd")
     public Date registrationDate; // Mudança do nome do campo
}

Aplicando em métodos Getter/Setter:

import com.alibaba.fastjson.annotation.JSONField;

public class ProductItem {
    private String productCode;

    @JSONField(name="PROD_CODE")
    public String getProductCode() { return productCode;}

    @JSONField(name="PROD_CODE") // Anotação também no setter para desserialização
    public void setProductCode(String productCode) {this.productCode = productCode;}
}

Observação: Se o atributo for privado, é essencial ter métodos set* correspondentes para permitir a desserialização.

3. Usando format para Datas

Personalize o formato de datas para cada campo:

import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;

 public class EventLog {
      // Configura data para ser serializada/desserializada no formato 'yyyyMMdd'
      @JSONField(format="yyyyMMdd")
      public Date timestamp;
 }

4. Usando serialize/deserialize para Excluir Campos

Controle a inclusão ou exclusão de campos na serialização ou desserialização:

import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;

public class SensitiveData {
      @JSONField(serialize=false) // Este campo não será incluído na saída JSON
      public Date creationTimestamp;
 }

import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;

 public class InputConfig {
      @JSONField(deserialize=false) // Este campo será ignorado na entrada JSON
      public Date lastModification;
 }

5. Usando ordinal para Definir a Ordem dos Campos

Por padrão, Fastjson serializa campos em ordem alfabética. A partir da versão 1.1.42, ordinal permite definir a ordem de saída dos campos.

import com.alibaba.fastjson.annotation.JSONField;

public static class OrderedFields {
    @JSONField(ordinal = 3)
    private int fieldC; // Alteração do nome
    @JSONField(ordinal = 2)
    private int fieldB; // Alteração do nome
    @JSONField(ordinal = 1)
    private int fieldA; // Alteração do nome

    // Getters e setters
    public int getFieldC() { return fieldC; }
    public void setFieldC(int fieldC) { this.fieldC = fieldC; }
    public int getFieldB() { return fieldB; }
    public void setFieldB(int fieldB) { this.fieldB = fieldB; }
    public int getFieldA() { return fieldA; }
    public void setFieldA(int fieldA) { this.fieldA = fieldA; }
}

6. Usando serializeUsing com um Serializador Personalizado

A partir da versão 1.2.16, @JSONField suporta serializeUsing para aplicar um serializador específico a um atributo de uma classe. Isso permite personalizar a representação JSON de um valor específico.

import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import java.io.IOException;
import java.lang.reflect.Type;

public static class PriceInformation { // Alteração do nome da classe
    @JSONField(serializeUsing = PriceValueSerializer.class)
    public int finalPrice; // Alteração do nome do campo
}

public static class PriceValueSerializer implements ObjectSerializer { // Alteração do nome da classe
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
                      int features) throws IOException {
        Integer price = (Integer) object;
        String formattedPrice = price + " EUR"; // Modificação do valor
        serializer.write(formattedPrice);
    }
}

Código de Teste:

import com.alibaba.fastjson.JSON;
import org.junit.Assert; // Assumindo JUnit para asserts

// ... classes PriceInformation e PriceValueSerializer definidas acima ...

public class SerializationTest {
    public static void main(String[] args) {
        PriceInformation itemPrice = new PriceInformation();
        itemPrice.finalPrice = 250; // Novo valor
        String jsonResult = JSON.toJSONString(itemPrice);
        System.out.println(jsonResult);
        Assert.assertEquals("{\"finalPrice\":\"250 EUR\"}", jsonResult);
    }
}

Personalização com @JSONType

Similar a @JSONField, mas @JSONType é aplicada no nível da classe para configurações que afetam todo o objeto, não apenas um campo específico.

Serialização Personalizada com SerializeFilter

1. Introdução

SerializeFilter é uma abordagem programática para personalizar a serialização. Fastjson oferece seis tipos de SerializeFilter para diferentes cenários:

  • PropertyPreFilter: Decide serializar com base no nome da propriedade.
  • PropertyFilter: Decide serializar com base no nome e valor da propriedade.
  • NameFilter: Modifica o nome da chave (Key) na saída JSON.
  • ValueFilter: Modifica o valor (Value) na saída JSON.
  • BeforeFilter: Adiciona conteúdo antes da serialização dos atributos do objeto.
  • AfterFilter: Adiciona conteúdo após a serialização dos atributos do objeto.

2. PropertyFilter: Filtragem por Nome e Valor da Propriedade

public interface PropertyFilter extends SerializeFilter {
    boolean apply(Object sourceObject, String propertyKey, Object propertyValue);
}

Permite decidir a serialização com base no objeto, nome da propriedade ou valor da propriedade. Exemplo:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PropertyFilter;

// ... Assumindo uma classe 'UserRecord' com um campo 'userId' (int)

PropertyFilter userFilter = new PropertyFilter() {
    @Override
    public boolean apply(Object sourceObject, String propertyKey, Object propertyValue) {
        if ("userId".equals(propertyKey)) {
            int userId = ((Integer) propertyValue).intValue();
            return userId >= 1000; // Serializa apenas se userId for maior ou igual a 1000
        }
        return false; // Não serializa outras propriedades por padrão
    }
};

// ... Criar um objeto UserRecord
// String filteredJson = JSON.toJSONString(userRecordObject, userFilter);

3. PropertyPreFilter: Filtragem Apenas por Nome da Propriedade

Diferente de PropertyFilter, este filtro age antes da chamada do getter, evitando possíveis exceções se o getter não for seguro.

import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyPreFilter;

 public interface PropertyPreFilter extends SerializeFilter {
      boolean apply(JSONSerializer currentSerializer, Object sourceObject, String propertyName);
  }

4. NameFilter: Modificação do Nome da Chave

Para alterar o nome das chaves (propriedades) no JSON de saída, o método process deve retornar o novo nome.

import com.alibaba.fastjson.serializer.NameFilter;

public interface NameFilter extends SerializeFilter {
    String process(Object object, String propertyName, Object propertyValue);
}

Fastjson inclui um PascalNameFilter para converter nomes de campos para o estilo PascalCase (primeira letra maiúscula). Exemplo:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PascalNameFilter;

// Supondo um objeto 'productDetails' com campos como 'productName'
String pascalCaseJson = JSON.toJSONString(productDetails, new PascalNameFilter());
// Saída: {"ProductName":"..."}, em vez de {"productName":"..."}

5. ValueFilter: Modificação do Valor

Permite interceptar e modificar os valores das propriedades antes da serialização.

import com.alibaba.fastjson.serializer.ValueFilter;

public interface ValueFilter extends SerializeFilter {
  Object process(Object object, String propertyName, Object propertyValue);
}

6. BeforeFilter: Adicionar Conteúdo Antes

Executa ações ou adiciona pares chave-valor ao JSON antes de serializar as propriedades padrão do objeto.

import com.alibaba.fastjson.serializer.BeforeFilter;

public abstract class BeforeFilter implements SerializeFilter {
   protected final void writeKeyValue(String key, Object value) { /* Implementação interna */ }
    // Implemente este método abstrato para adicionar conteúdo via writeKeyValue
    public abstract void writeBefore(Object object);
}

7. AfterFilter: Adicionar Conteúdo Depois

Executa ações ou adiciona pares chave-valor ao JSON depois de serializar as propriedades padrão do objeto.

import com.alibaba.fastjson.serializer.AfterFilter;

 public abstract class AfterFilter implements SerializeFilter {
  protected final void writeKeyValue(String key, Object value) { /* Implementação interna */ }
    // Implemente este método abstrato para adicionar conteúdo via writeKeyValue
    public abstract void writeAfter(Object object);
}

Desserialização Personalizada com ParseProcess

1. Introdução

ParseProcess fornece interfaces para estender e personalizar o processo de desserialização. Fastjson suporta:

  • ExtraProcessor: Lida com campos no JSON que não existem no objeto Java.
  • ExtraTypeProvider: Fornece informações de tipo para campos extras.

2. Usando ExtraProcessor para Campos Adicionais

Quando o JSON de entrada contém campos que não mapeiam diretamente para propriedades de um objeto Java, ExtraProcessor pode capturá-los.

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
import java.util.HashMap;
import java.util.Map;

public static class UserProfile { // Alteração do nome da classe
    private int profileId; // Alteração do nome do campo
    private Map<String, Object> extraProperties = new HashMap<>(); // Alteração do nome do campo

    public int getProfileId() { return profileId; }
    public void setProfileId(int profileId) { this.profileId = profileId;}
    public Map<String, Object> getExtraProperties() { return extraProperties;}
}

ExtraProcessor customProcessor = new ExtraProcessor() {
    @Override
    public void processExtra(Object targetObject, String keyName, Object valueData) { // Alteração de nomes de parâmetros
        UserProfile user = (UserProfile) targetObject;
        user.getExtraProperties().put(keyName, valueData);
    }
};

UserProfile userProfile = JSON.parseObject("{\"profileId\":456,\"userName\":\"John Doe\"}", UserProfile.class, customProcessor);
System.out.println("ID do perfil: " + userProfile.getProfileId());
System.out.println("Nome de usuário (extra): " + userProfile.getExtraProperties().get("userName"));

// Assert.assertEquals(456, userProfile.getProfileId());
// Assert.assertEquals("John Doe", userProfile.getExtraProperties().get("userName"));

3. Usando ExtraTypeProvider para Definir Tipos de Campos Extras

ExtraTypeProvider complementa ExtraProcessor, permitindo especificar o tipo Java para um campo extra, influenciando como o valor é desserializado.

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
import com.alibaba.fastjson.parser.deserializer.ExtraTypeProvider;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public static class ItemData { // Alteração do nome da classe
    private int itemId; // Alteração do nome do campo
    private Map<String, Object> auxiliaryData = new HashMap<>(); // Alteração do nome do campo

    public int getItemId() { return itemId; }
    public void setItemId(int itemId) { this.itemId = itemId;}
    public Map<String, Object> getAuxiliaryData() { return auxiliaryData;}
}

class CustomItemProcessor implements ExtraProcessor, ExtraTypeProvider { // Alteração do nome da classe
    @Override
    public void processExtra(Object targetObject, String keyName, Object valueData) {
        ItemData item = (ItemData) targetObject;
        item.getAuxiliaryData().put(keyName, valueData);
    }

    @Override
    public Type getExtraType(Object targetObject, String keyName) {
        if ("itemQuantity".equals(keyName)) { // Alteração do nome do campo extra
            return Integer.class; // Garante que "itemQuantity" seja desserializado como Integer
        }
        return null;
    }
}
ExtraProcessor itemProcessor = new CustomItemProcessor();

ItemData product = JSON.parseObject("{\"itemId\":789,\"itemQuantity\":\"500\"}", ItemData.class, itemProcessor);
System.out.println("ID do item: " + product.getItemId());
System.out.println("Quantidade do item (convertida): " + product.getAuxiliaryData().get("itemQuantity"));

// Assert.assertEquals(789, product.getItemId());
// Assert.assertEquals(500, product.getAuxiliaryData().get("itemQuantity")); // Note a conversão para Integer

Integração do Fastjson com Spring MVC

Para aplicativos web que utilizam Spring MVC e exigem alta performance na serialização/desserialização JSON, o FastJsonHttpMessageConverter do Fastjson pode substituir o conversor padrão do Spring MVC. Isso otimiza a velocidade de processamento de JSON para anotações como @RestController, @ResponseBody e @RequestBody.

Configuração Baseada em XML:

Adicione a seguinte configuração ao arquivo XML do Spring MVC:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"/>      
    </mvc:message-converters>
</mvc:annotation-driven>

Para configurações personalizadas, adicione um bean FastJsonConfig:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <property name="fastJsonConfig" ref="customFastJsonConfig"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="customFastJsonConfig" class="com.alibaba.fastjson.support.config.FastJsonConfig">
    <!--   Adicione suas configurações personalizadas aqui, ex: setDateFormat, setSerializerFeatures   -->
</bean>

Configuração Programática (comum em Spring Boot):

Estenda WebMvcConfigurerAdapter (ou implemente WebMvcConfigurer a partir do Spring 5) e sobrescreva o método configureMessageConverters:

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; // Ou WebMvcConfigurer

import java.util.List;

@Configuration
public class WebConfigurationAdapter extends WebMvcConfigurerAdapter { // Renomeado
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        // Opcional: Configurações personalizadas
        // FastJsonConfig customConfig = new FastJsonConfig();
        // customConfig.set...
        // fastJsonConverter.setFastJsonConfig(customConfig);
        converters.add(0, fastJsonConverter); // Adiciona na primeira posição
    }
}

Observações importantes:

1. Versões do Fastjson: Se estiver usando Fastjson com versão anterior a 1.2.36 (fortemente recomendado atualizar), e Spring MVC 4.X, use FastJsonHttpMessageConverter4.

2. Ordem dos Conversores no Spring Boot: No Spring Boot 2.0.1 e versões posteriores, a ordem de carregamento dos WebMvcConfigurer mudou. É crucial adicionar o FastJsonHttpMessageConverter na primeira posição (converters.add(0, converter)) para garantir que ele seja priorizado sobre o Jackson, que é o padrão.

Integração do Fastjson com Spring Data Redis

Para quem utiliza RedisTemplate do Spring Data Redis e necessita de alta performance na serialização/desserialização JSON, o Fastjson oferece GenericFastJsonRedisSerializer ou FastJsonRedisSerializer como alternativas para RedisSerializer.

Configuração Baseada em XML:

Substitua o Serializer padrão do RedisTemplate por GenericFastJsonRedisSerializer:

<bean id="redisOperationsTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <!-- Renomeado -->
    <property name="connectionFactory" ref="jedisConnFactory"/> <!-- Renomeado -->
    <property name="defaultSerializer">
        <bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
    </property>
</bean>

Exemplo de configuração completa para Spring com Redis:

<!-- Configuração do Pool de Conexões Redis (Opcional) -->
<bean id="jedisPoolConfigCustom" class="redis.clients.jedis.JedisPoolConfig"> <!-- Renomeado -->
    <property name="maxTotal" value="${redis.pool.maxActive}"/>
    <property name="maxIdle" value="${redis.pool.maxIdle}"/>
    <property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
    <!-- Outras configurações de pool... -->
</bean>

<!-- Configuração da Fábrica de Conexões Redis -->
<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <!-- Renomeado -->
    <!-- Define a configuração do pool; sem isso, o pool padrão é usado. Defina usePool = false para desativar. -->   
    <property name="poolConfig" ref="jedisPoolConfigCustom" />  
    <property name="hostName" value="${redis.host}"/> <!-- Variável renomeada -->
    <property name="port" value="${redis.port}"/> <!-- Variável renomeada -->
    <property name="password" value="${redis.password}"/> <!-- Variável renomeada -->
    <property name="database" value="${redis.database}"/> <!-- Variável renomeada -->
    <!-- Outras configurações da fábrica de conexões... -->
</bean>

<!-- Configuração do RedisTemplate -->
<bean id="redisOperationsTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <!-- Renomeado -->
    <!-- Define a fábrica de conexões Redis -->
    <property name="connectionFactory" ref="jedisConnFactory"/> <!-- Renomeado -->
    <!-- Define o Serializer padrão, incluindo keySerializer e valueSerializer -->
    <property name="defaultSerializer">
        <bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
    </property>
    <!-- Opcional: Definição separada para keySerializer -->
    <property name="keySerializer">
        <bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
    </property>
    <!-- Opcional: Definição separada para valueSerializer -->
    <property name="valueSerializer">
        <bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
    </property>
</bean>

Configuração Programática (comum em Spring Boot):

Crie um bean RedisTemplate explicitamente em uma classe de configuração (anotada com @Configuration) e defina o Serializer:

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class RedisConfig { // Renomeado
    @Bean
    public RedisTemplate<String, Object> customRedisTemplate(RedisConnectionFactory redisConnectionFactory) { // Renomeado
        RedisTemplate<String, Object> template = new RedisTemplate<>(); // Renomeado
        template.setConnectionFactory(redisConnectionFactory);

        GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
        template.setDefaultSerializer(serializer); // Define o Serializer padrão (key e value)

        // Opcional: Define keySerializer e valueSerializer separadamente
        // template.setKeySerializer(serializer);
        // template.setValueSerializer(serializer);
        return template;
    }
}

Normalmente, GenericFastJsonRedisSerializer é suficiente. Para tipos específicos, FastJsonRedisSerializer pode ser usado de maneira similar, permitindo maior controle sobre a serialização de tipos específicos de objetos no Redis.

Tags: FastJSON java JSON serialização Desserialização

Publicado em 7-9 18:21