Skip to main content

Best Practices

The following guidelines help you get the most out of the Juneau marshalling APIs in terms of performance and maintainability.

Reuse serializer and parser instances​

Serializers and parsers are immutable and thread-safe, and they maintain internal caches of bean metadata. Reusing a single instance across requests (rather than rebuilding one per call) lets those caches do their job and avoids the cost of re-analyzing the same bean classes repeatedly.

// Good — build once, reuse everywhere (e.g. a static field or injected singleton).
static final JsonSerializer SERIALIZER = JsonSerializer.create().ws().build();

String json = SERIALIZER.write(myBean);

The predefined DEFAULT instances (JsonSerializer.DEFAULT, JsonParser.DEFAULT, …) are already reusable singletons — prefer them when you don't need custom settings. When you do need a variant, use copy() on an existing instance so the derived instance shares the same construction path.

Choose the right format for the job​

  • In general, JSON serialization and parsing is roughly 20% faster than XML, and JSON output is more compact.
  • MessagePack is the fastest and most compact of the built-in formats — prefer it for high-throughput or bandwidth-sensitive machine-to-machine traffic where human readability isn't required.
  • RDF has very different performance characteristics from the core marshallers — node traversal in RDF models is inefficient, so building tree structures out of large RDF graphs is comparatively slow. RDF is documented separately in the RDF section; the guidance on this page applies to the core juneau-marshall formats.

Enable optional features only when needed​

  • The MarshallingTraverseContext.Builder.detectRecursions() option adds a performance penalty of around 20% because every object on the traversal stack has to be checked for cycles. Enable it only when serializing models that can actually contain recursive references. See Non-Tree Models and Recursion Detection.
  • The MarshallingContext.Builder.validateSchema() option validates bean property values against their @Schema-declared constraints during parsing and serialization. This adds per-property validation overhead and is disabled by default — turn it on only on the boundaries where you actually need input/output validation (typically inbound parsing), not on every internal marshalling operation.

Prefer ClassMeta parse overloads on hot paths​

The Parser methods that take a ClassMeta parameter are slightly faster than the overloads that take a Class or Object, because the latter must perform a hash lookup to resolve the target type to its ClassMeta. On tight loops that repeatedly parse the same type, resolve the ClassMeta once (via BeanContext.getClassMeta(...)) and reuse it.

Stream large datasets instead of buffering​

For large collections or continuous feeds, don't build a giant in-memory POJO and serialize it in one shot. Juneau's streaming APIs let you emit or consume records one at a time with a bounded memory footprint — see Token & Record Streaming and Large-Dataset Streaming.

When to use MarshallUtils​

The MarshallUtils class provides concise static shortcuts (json(...), xml(...), msgPack(...), …) that are ideal for one-off serialization/parsing, tests, and scripts — especially with static imports. Because they delegate to the shared DEFAULT marshaller instances, they carry no extra construction cost. Reach for an explicit serializer/parser instance instead when you need custom settings (whitespace, quotes, swaps, dictionaries, etc.), since MarshallUtils always uses default configuration.

See also​