Parsing JSON rapido in Java con le nuove regole di sicurezza in Fastjson2

Se lavori con Java da più di cinque anni, il nome Fastjson probabilmente ti evoca sentimenti contrastanti. Da un lato, la libreria era effettivamente più veloce di Jackson. Dall'altro, le vulnerabilità di deserializzazione regolari attraverso AutoType hanno reso il suo utilizzo un mal di testa costante per gli specialisti della sicurezza.
Il team di Alibaba conosceva questi problemi meglio di chiunque altro. Due anni fa, hanno essenzialmente riscritto la libreria da zero. Ecco Fastjson2: un motore ripensato per lavorare con JSON, dove sicurezza e prestazioni sulle versioni moderne di Java sono state prese come fondamento.
Come è cambiata la sicurezza
Il peccato principale della prima versione era il meccanismo AutoType. Poteva creare oggetti Java arbitrari dal payload JSON direttamente durante il parsing. Gli attaccanti trovavano regolarmente modi per aggirare la lista integrata di classi bloccate e triggherare RCE.
In Fastjson2, l'approccio è cambiato drasticamente. AutoType è ora disabilitato di default e la scansione delle classi funziona senza whitelist hardcoded. È stato introdotto SafeMode, che proibisce completamente l'assemblaggio automatico di oggetti arbitrari.
Tutti i flag di configurazione sono ora impostati su OFF di default. La libreria non tenta più di indovinare i tipi dove ciò potrebbe causare danni.
Funzionalità principali
Gli sviluppatori si sono concentrati sulle versioni moderne di JDK, da Java 8 a Java 21. Il motore è ottimizzato per l'uso di Vector API, i tipi Record e le stringhe compatte.
Ecco cosa spicca quando si prende confidenza con la libreria:
- Supporto per formati duali. Sia JSON testuale normale che il protocollo binario JSONB.
- Parsing selettivo rapido tramite JSONPath senza creare oggetti Java intermedi.
- Moduli di estensione per Kotlin con un comodo DSL e supporto completo per Spring Framework versioni 5 e 6.
- Compilazione nativa con GraalVM Native Image out of the box.
Quick Start nel codice
Aggiungere la libreria richiede l'inclusione di una singola dipendenza in pom.xml:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.61</version>
</dependency>
Un esempio semplice di parsing e serializzazione appare familiare:
import com.alibaba.fastjson2.JSON;
// Парсинг строки в объект
User user = JSON.parseObject("{\"name\":\"John\",\"age\":25}", User.class);
// Сериализация объекта в JSON
String json = JSON.toJSONString(user);
Se scrivi in Kotlin, il modulo fastjson2-kotlin offre estensioni concise. Il codice risulta notevolmente più pulito grazie alla funzione di estensione to<T>():
import com.alibaba.fastjson2.*
val text = """{"id":1,"name":"John"}"""
val user = text.to<User>()
val jsonBytes = user.toJSONByteArray()
Formato binario JSONB per i microservizi
Il JSON testuale rimane lo standard per le API REST, ma nella comunicazione tra servizi perde terreno a causa delle dimensioni eccessive e del sovraccarico di conversione delle stringhe. Fastjson2 contiene un'implementazione integrata del formato binario JSONB.
JSONB codifica i tipi di dati e la struttura in un set compatto di byte. Questo fornisce risparmi sostanziali di traffico e accelera i servizi RPC.
User user = new User(1, "John");
// Сериализация в байтовый массив JSONB
byte[] bytes = JSONB.toBytes(user);
// Сериализация с дополнительным сжатием структурированных данных
byte[] compactBytes = JSONB.toBytes(user, JSONWriter.Feature.BeanToArray);
// Обратный парсинг
User parsedUser = JSONB.parseObject(bytes, User.class);
La compressione con l'opzione BeanToArray trasforma un oggetto in un array di valori senza ripetere i nomi dei campi, avvicinando l'efficienza del formato a quella di Protobuf o Kryo.
Lettura selettiva tramite JSONPath
Spesso, da un'enorme risposta JSON di un microservizio, hai bisogno solo di estrarre uno o due campi. I parser classici come Jackson prima costruiscono un albero completo in memoria o creano un oggetto con tutti i campi, consumando risorse CPU e memoria.
Fastjson2 risolve questo problema con un'implementazione dello standard SQL:2016 JSONPath. Può estrarre dati direttamente da un byte stream grezzo:
byte[] payload = fetchLargeJsonFromNetwork();
// Создаём и кэшируем путь
JSONPath path = JSONPath.of("$.data.user.id");
// Извлекаем значение напрямую из байтового массива
JSONReader reader = JSONReader.of(payload);
Object userId = path.extract(reader);
Questo trucco è utile nelle pipeline di elaborazione dati ad alto carico dove allocazioni di oggetti non necessarie nella JVM portano immediatamente a pause frequenti del Garbage Collector.
Migrazione dalla prima versione
Raggiungere la piena compatibilità all'indietro cambiando le versioni major è impossibile, ma gli autori hanno cercato di facilitare la transizione. È stato creato un modulo di compatibilità per i progetti che si affidano alla vecchia API:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.61</version>
</dependency>
Usa i vecchi pacchetti groupId e com.alibaba.fastjson, ma internamente gira sul nuovo motore v2. Questo rende possibile l'aggiornamento senza riscrivere centinaia di import nel codice legacy.
Tuttavia, durante la migrazione, devi tenere conto di cambiamenti importanti:
- Il parsing dei riferimenti ciclici è disabilitato di default.
- Il matching intelligente dei campi non funziona più out of the box.
- Tutte le opzioni del serializzatore devono essere esplicitamente abilitate attraverso
JSONWriter.Feature.
Fastjson2 si è rivelato un prodotto maturo e solido. Gli sviluppatori hanno imparato dagli errori passati, hanno eliminato le impostazioni pericolose di default e hanno dato all'ecosistema Java uno strumento veramente veloce.
Prova sicuramente Fastjson2 se stai sviluppando servizi ad alto carico, lavorando con Spring 6 / GraalVM Native Image, o cercando un'alternativa ai formati di serializzazione pesanti nei progetti Kotlin.
Progetti correlati