Input (schema .proto)

Output (Java)

Cosa fa questo strumento

Hai uno schema Protocol Buffers e un servizio Java che consuma quei messaggi — magari via gRPC, magari via JSON su HTTP. Far girare il protoc ufficiale con il plugin Java ti dà classi generate in pattern Builder, ottime per la produzione ma pesanti per fare schizzi, prototipi o per mappare a mano del JSON su un POJO. Questo convertitore emette classi Java semplici — campi pubblici, default sensati, una classe per messaggio, tipi separati per gli enum.

Il mapping dei tipi segue come è scritto il Java a mano nel mondo reale: stringString, boolboolean, int32/sint32/sfixed32int, int64/sint64/sfixed64long, doubledouble, floatfloat, bytesbyte[]. Java non ha tipi numerici unsigned, quindi uint32/fixed32 cadono su int e uint64/fixed64 cadono su long — va bene per la maggior parte dei casi; se davvero ti serve il bit alto, usa Integer.toUnsignedLong al confine. repeated T diventa List<T>, map<K, V> diventa Map<K, V>, entrambi da java.util.

I nomi dei campi vengono convertiti da snake_case (convenzione Protobuf) a camelCase (convenzione Java) — coerentemente con quello che farebbe protoc, e con quello che usa il JSON mapping di proto3. Gli enum diventano tipi public enum di primo livello con il valore intero del wire conservato come campo final, così puoi fare round-trip via codice generato da protoc se un giorno passi a quello. I messaggi annidati vengono appiattiti a classi di primo livello così ognuna può andare nel proprio file quando dividi l'output. Tutto gira nel tuo browser — il tuo schema non lascia la pagina.

Come si usa

Tre passi. L'output è pronto per essere buttato in un progetto Java.

1

Incolla il tuo schema .proto

Butta lo schema nell'editor di sinistra. syntax = "proto3"; in cima va bene ma è opzionale. Il parser gestisce blocchi message annidati, dichiarazioni enum, oneof, map<K, V> e opzioni di campo. Le direttive import vengono riconosciute ma saltate — incolla i tipi importati inline se ti servono.

La conversione dei nomi dei campi è automatica: order_id nel .proto diventa orderId in Java. I nomi di messaggi ed enum restano come sono (già in PascalCase).

2

Leggi l'output

Sulla destra: un singolo blocco .java con prima tutti gli enum, poi tutte le classi in ordine di dichiarazione. Ogni classe ha campi pubblici con valori default per i primitivi (0, 0.0, false, ""), e i tipi reference (List, Map, ref a messaggi) restano null finché non li popoli. Gli import java.util.List e java.util.Map vengono aggiunti solo quando lo schema ne ha bisogno.

3

Buttalo nel tuo progetto

Copia ogni classe nel suo file .java (Java richiede una classe pubblica per file). Aggiungi una dichiarazione package, poi collega le classi al tuo deserializzatore JSON preferito — Jackson prende i campi pubblici di default, e il JSON mapping di proto3 usa camelCase, quindi i nomi dei campi già coincidono. Se preferisci getter/setter, il refactor "Encapsulate Fields" di IntelliJ lo fa con un colpo di tastiera.

Quando ti fa risparmiare davvero tempo

Abbozzare un client Java per un servizio gRPC

Stai facendo uno spike di un client Java contro un backend gRPC esistente — magari un servizio Spring Boot, magari uno Quarkus — e non vuoi ancora montare l'intero plugin Maven o Gradle di protoc. Incolla lo schema, butta le classi in src/main/java/dto, deserializza la risposta JSON con Jackson, spedisci il prototipo.

Scrivere a mano DTO che combaciano con un proto

Il tuo team usa Protobuf come fonte di verità sul filo, ma il servizio Java consumatore ha bisogno solo di tre o quattro campi e tu non vuoi una dipendenza Message/Builder. Incolla lo schema, cancella i campi che non ti servono, hai un DTO semplice che compila standalone.

Revisione di una modifica all'API Protobuf

Un collega del backend ha aggiunto campi a un messaggio. Vuoi vedere come influisce sul POJO Java senza far partire la build. Incolla il nuovo .proto, fai il diff dell'output Java contro le tue classi attuali, lascia un commento di review mirato.

Confrontare con l'output generato da protoc

La tua build usa il plugin Java ufficiale di protoc, che produce classi in pattern Builder. Incolla qui lo schema per avere un riferimento pulito di come appare il Java semplice, utile per la documentazione, l'onboarding o per i colleghi Kotlin che preferiscono le data class.

Domande frequenti

Il mio schema viene mandato da qualche parte?

No. Parser ed emitter Java girano interamente nel tuo browser come JavaScript. Apri i DevTools e guarda la scheda Network mentre incolli — zero richieste. Utile quando il tuo schema include path di package interni, nomi di tipi o qualsiasi cosa tu non voglia spedire a un servizio di terze parti.

Perché campi pubblici e non getter/setter?

Due ragioni. Primo, l'output è pensato come punto di partenza che adatti — i campi pubblici sono la cosa più piccola che compila, e puoi sempre lanciare "Encapsulate Fields" nel tuo IDE. Secondo, il consumatore più comune è la deserializzazione Jackson, che funziona out of the box sui campi pubblici. Se ti serve un record, un tipo immutabile o una classe Lombok @Data, incolla l'output e refattorizza.

Come vengono gestiti uint32 e uint64?

Java non ha tipi interi unsigned, quindi uint32/fixed32 mappano su int e uint64/fixed64 mappano su long. Per valori che entrano nel range con segno funziona; per valori sopra Integer.MAX_VALUE o Long.MAX_VALUE vedrai numeri negativi. La soluzione standard è Integer.toUnsignedLong(x) al confine, oppure usare gli helper aritmetici di java.lang.Long — vedi la documentazione delle API Java 21 per gli helper unsigned.

Perché i campi int64 sono solo long, non String?

TypeScript e JSON hanno un tetto di precisione a 53 bit, quindi la spec JSON di proto3 codifica gli int a 64 bit come stringhe per preservare la precisione. Il long di Java è un vero intero con segno a 64 bit senza perdita di precisione, quindi manteniamo il tipo Java come long. Se stai deserializzando JSON in cui il server ha già codificato l'int64 come stringa, configura Jackson con @JsonFormat o un deserializzatore custom; il tipo sottostante non deve cambiare.

Come gestisce i messaggi annidati?

Ogni messaggio annidato viene appiattito in una classe di primo livello. La convenzione Java è una classe pubblica per file, quindi i tipi piatti di primo livello sono più facili da dividere quando copi ogni classe nel suo file .java. Se preferisci static inner class (lo stile che emette protoc), incolla l'output e sposta le classi annidate dentro il loro genitore — i riferimenti ai campi usano già il nome leaf e si risolveranno una volta nello stesso scope.

I campi sono marcati come optional?

No — i campi proto3 hanno sempre un default nel formato del wire, quindi i campi primitivi sono inizializzati al loro valore zero Java (0, 0.0, false, ""). I tipi reference (List, Map, messaggi annidati, byte[]) partono come null; ricordati di inizializzarli prima di aggiungere elementi. Se vuoi wrapper espliciti Optional<T>, è un passo manuale.

Gestisce oneof?

Ogni campo di oneof viene emesso come un normale campo di classe. L'output non impone il vincolo "esattamente uno" che oneof implica — per quello servirebbe una gerarchia di tipi sealed o un check a runtime, nessuno dei due si sposa con un POJO semplice. Se vuoi una modellazione più rigida, prendi l'output e converti i campi del oneof in una sealed interface con un record per caso.

Posso usare queste classi con il runtime ufficiale protobuf-java?

Non direttamente — il runtime ufficiale si aspetta classi generate da protoc con Builder, parseFrom e il resto del contratto com.google.protobuf.MessageOrBuilder. Le classi di questo strumento sono POJO semplici, pensate per la serializzazione JSON (Jackson, Gson, Moshi). Per il formato binario del wire vuoi comunque il codegen ufficiale — vedi il tutorial Java per il setup.

Strumenti correlati

Se stai lavorando con Protobuf, JSON e Java, questi vanno bene insieme: