Protobuf to Java Converter
Paste a .proto schema. Get plain Java classes you can drop into a project — public fields, no getters, easy to adapt.
Input (.proto schema)
Output (Java)
What this tool does
You have a Protocol Buffers schema and a Java service that consumes those messages — maybe over gRPC, maybe via JSON over HTTP. Running the official protoc with the Java plugin gives you generated Builder-pattern classes that are great for production but heavy for sketching, prototyping, or hand-mapping JSON onto a POJO. This converter emits plain Java classes — public fields, sensible defaults, one class per message, separate types for enums.
Type mapping follows what hand-written Java looks like in the wild: string → String, bool → boolean, int32/sint32/sfixed32 → int, int64/sint64/sfixed64 → long, double → double, float → float, bytes → byte[]. Java has no unsigned numeric types, so uint32/fixed32 drop to int and uint64/fixed64 drop to long — fine for most use cases; if you actually need the high bit, use Integer.toUnsignedLong at the boundary. repeated T becomes List<T>, map<K, V> becomes Map<K, V>, both from java.util.
Field names convert from snake_case (Protobuf convention) to camelCase (Java convention) — matching what protoc would do, and what the proto3 JSON mapping uses. Enums become top-level public enum types with the integer wire value preserved as a final field, so you can round-trip through protoc-generated code if you ever switch over. Nested messages are flattened to top-level classes so each one can move into its own file when you split the output. Everything runs in your browser — your schema does not leave the page.
How to use it
Three steps. The output is ready to drop into a Java project.
Paste your .proto schema
Drop the schema into the left editor. syntax = "proto3"; at the top is fine but optional. The parser handles nested message blocks, enum declarations, oneof, map<K, V>, and field options. import directives are recognised but skipped — paste imported types inline if you need them.
Field name conversion is automatic: order_id in .proto becomes orderId in Java. Message and enum names stay as-is (already PascalCase).
Read the output
On the right: a single .java block with all enums first, then all classes in declaration order. Each class has public fields with default values for primitives (0, 0.0, false, ""), and reference types (List, Map, message refs) left as null until you populate them. The java.util.List and java.util.Map imports are added only when the schema needs them.
Drop into your project
Copy each class into its own .java file (Java requires one public class per file). Add a package declaration, then wire the classes to your JSON deserialiser of choice — Jackson picks up public fields by default, and the proto3 JSON mapping uses camelCase, so the field names already match. If you prefer getters/setters, IntelliJ's "Encapsulate Fields" refactor handles that in one keystroke.
When this actually saves time
Sketching a Java client for a gRPC service
You are spiking a Java client against an existing gRPC backend — maybe a Spring Boot service, maybe a Quarkus one — and you do not want to set up the full protoc Maven or Gradle plugin yet. Paste the schema, drop the classes into src/main/java/dto, deserialise the JSON response with Jackson, ship the prototype.
Hand-rolling DTOs that match a proto
Your team uses Protobuf as the source of truth on the wire, but the consuming Java service only needs three or four fields and you do not want a Message/Builder dependency. Paste the schema, delete the fields you do not need, you have a plain DTO that compiles standalone.
Reviewing a Protobuf API change
A backend teammate added fields to a message. You want to see how that affects the Java POJO without running the build. Paste the new .proto, diff the Java output against your current classes, leave a focused review comment.
Cross-checking the protoc-generated output
Your build uses the official protoc Java plugin, which produces Builder-pattern classes. Paste the schema here for a clean reference of what plain Java looks like, useful for documentation, onboarding, or for Kotlin teammates who prefer data classes.
Common questions
Is my schema sent anywhere?
No. The parser and Java emitter run entirely in your browser as JavaScript. Open DevTools and watch the Network tab while you paste — zero requests. Useful when your schema includes internal package paths, type names, or anything you would not want to ship to a third-party service.
Why public fields and not getters/setters?
Two reasons. First, the output is meant to be a starting point you adapt — public fields are the smallest possible thing that compiles, and you can always run "Encapsulate Fields" in your IDE. Second, the most common consumer is Jackson deserialisation, which works out of the box on public fields. If you need a record, immutable type, or Lombok @Data class, paste the output and refactor.
How are uint32 and uint64 handled?
Java has no unsigned integer types, so uint32/fixed32 map to int and uint64/fixed64 map to long. For values that fit in the signed range this works fine; for values above Integer.MAX_VALUE or Long.MAX_VALUE you will see negative numbers. The standard fix is Integer.toUnsignedLong(x) at the boundary, or use java.lang.Long arithmetic helpers — see the Java 21 API docs for the unsigned helpers.
Why are int64 fields just long, not String?
TypeScript and JSON have a 53-bit precision ceiling, so the proto3 JSON spec encodes 64-bit ints as strings to preserve precision. Java's long is a true 64-bit signed integer with no precision loss, so we keep the Java type as long. If you are deserialising JSON where the server already encoded the int64 as a string, configure Jackson with @JsonFormat or a custom deserialiser; the underlying type does not need to change.
How does it handle nested messages?
Each nested message is flattened to a top-level class. Java's convention is one public class per file, so flat top-level types are easier to split when you copy each class into its own .java file. If you would rather have static inner classes (the style protoc emits), paste the output and move the nested classes inside their parent — the field references already use the leaf name and will resolve once they are in the same scope.
Are fields marked optional?
No — proto3 fields always have a default in the wire format, so primitive fields are initialised to their Java zero value (0, 0.0, false, ""). Reference types (List, Map, nested messages, byte[]) start as null; remember to initialise them before adding elements. If you want explicit Optional<T> wrappers, that is a manual step.
Does it handle oneof?
Each oneof field is emitted as a regular class field. The output does not enforce the "exactly one" constraint that oneof implies — for that you would need a sealed type hierarchy or a runtime check, neither of which fits a plain POJO. If you need stricter modelling, take the output and convert the oneof fields into a sealed interface with one record per case.
Can I use these classes with the official protobuf-java runtime?
Not directly — the official runtime expects classes generated by protoc with Builder, parseFrom, and the rest of the com.google.protobuf.MessageOrBuilder contract. The classes from this tool are plain POJOs, intended for JSON serialisation (Jackson, Gson, Moshi). For binary wire format you still want the official codegen — see the Java tutorial for setup.
Related tools
If you are working with Protobuf, JSON, and Java, these pair well: