Skip to main content

Supported JDK Datatypes

Juneau provides first-class, direct serializer/parser support for a wide range of JDK datatypes — no extra modules, no opt-in registration, no swap setup required for the common cases. This page enumerates the supported types, their default wire forms, and the per-type knobs that let you adjust the wire form across all formats.

Design principle: first-class > swaps​

Juneau handles common JDK datatypes through direct serializer/parser code paths plus per-type *Format enums (e.g. TemporalFormat, DurationFormat, BinaryFormat, …), not through Default Swaps. Swaps are reserved for two scenarios:

  1. End-user customization of how a third-party type should be marshalled.
  2. A handful of internal types whose wire form is genuinely a single canonical encoding with no useful knobs (e.g. StackTraceElement, MatchResult).

The primitive optionals (OptionalInt / OptionalLong / OptionalDouble) and BitSet are handled on the first-class path — the optionals mirror the Optional<T> plumbing directly, and BitSet carries its own bitSetFormat builder knob (see below).

The advantage of the first-class path is that the format choice is a builder setter rather than a swap-replacement dance:

// First-class: per-type format knob on the builder.
var s = JsonSerializer.create().temporalFormat(TemporalFormat.ISO_LOCAL_DATE_TIME).build();

// Versus the swap-based alternative you'd need without it.
var s = JsonSerializer.create().swaps(MyCustomInstantSwap.class).build();

This mirrors the Jackson jackson-datatype-* family conceptually but without the per-type module wiring — Juneau ships the support in-box.


Numbers​

TypeDefault wire formKnob
byte / Bytenumber—
short / Shortnumber—
int / Integernumber—
long / Longnumber—
float / FloatnumberfloatFormat
double / DoublenumberfloatFormat
BigIntegernumber (or string when overflow risk)bigNumberFormat
BigDecimalnumber (or string when overflow risk)bigNumberFormat

The bigNumberFormat knob (BigNumberFormat.DEFAULT | STRING | NUMBER) lets you force big-number wire emission as a quoted string for JavaScript-safe round-trips.

floatFormat covers NaN/Infinity handling (FloatFormat.DEFAULT writes lenient JSON; FloatFormat.STRICT forces a string).


Booleans, chars, strings​

TypeDefault wire formKnob
boolean / Booleantrue / falsebooleanFormat (allows 0/1)
char / Characterstring of length 1—
StringstringtrimStrings
CharSequence / StringBuilder / StringBufferstring—

Optional family​

TypeWire formEmpty formNotes
Optional<T>unwrapped T when presentomitted (or null if keepNullProperties)parse: absent or null → Optional.empty()
OptionalIntunwrapped int when presentomitted (or null if keepNullProperties)parse: absent or null → OptionalInt.empty()
OptionalLongunwrapped long when presentomitted (or null if keepNullProperties)parse: absent or null → OptionalLong.empty()
OptionalDoubleunwrapped double when presentomitted (or null if keepNullProperties)parse: absent or null → OptionalDouble.empty()

Optional.empty() is treated identically to null by the inclusion machinery (governed by keepNullProperties). See Null & Inclusion Policies for the full contract, including how per-property Nulls coercion interacts.


Dates, times, and durations (java.time)​

TypeDefault wire formKnob
InstantISO-8601 instant stringtemporalFormat
LocalDateISO-8601 date stringtemporalFormat
LocalDateTimeISO-8601 local-datetime stringtemporalFormat
LocalTimeISO-8601 local-time stringtemporalFormat
ZonedDateTimeISO-8601 zoned-datetime stringtemporalFormat
OffsetDateTimeISO-8601 offset-datetime stringtemporalFormat
OffsetTimeISO-8601 offset-time stringtemporalFormat
Year / YearMonth / MonthDayISO-8601 partial stringtemporalFormat
ZoneIdzone-id string (e.g. "America/New_York")—
ZoneOffsetzone-offset string (e.g. "+05:00")— (handled via ZoneId)
DurationISO-8601 duration string ("PT15M")durationFormat
PeriodISO-8601 period string ("P1Y2M")periodFormat

The temporalFormat knob (enum) selects ISO-8601 (DEFAULT), local without zone, RFC-1123, or epoch millis. The durationFormat and periodFormat knobs select ISO-8601 (default) or numeric milli / day forms.

Legacy java.util.Calendar / java.util.Date are also supported via calendarFormat / dateFormat.


Identifiers and URI types​

TypeDefault wire formKnob
UUIDstring (e.g. "550e8400-…")uuidFormat
URI / URLstring—
LocaleBCP-47 language tag stringlocaleFormat
TimeZonetimezone-id stringtimezoneFormat
CurrencyISO-4217 currency-code stringcurrencyFormat
Class<?>class-name stringclassFormat

Binary​

TypeDefault wire formKnob
byte[]Base-64 stringbinaryFormat (Base-64 / Base-64-URL / hex / SPACED-HEX)
InputStream / Readerinline content—
BitSetset-bit-index list string (e.g. "0,2,5")bitSetFormat (INDICES / BITS / HEX)

The bitSetFormat knob selects the textual wire form: INDICES (default, ascending set-bit indices as a comma-delimited token), BITS (little-endian bit string, e.g. "101001"), or HEX (hex-encoded little-endian byte array). It is configurable at all four precedence levels (@MarshalledProp, @Marshalled, @MarshalledConfig, and the bitSetFormat(...) builder setter), exactly like the other *Format enums.


Enums and collections​

TypeDefault wire formNotes
enumstring (enumFormat = NAME) or ordinal intper-type via enumFormat
EnumSet<E>JSON array of enum namesparses back to EnumSet for declared parameter EnumSet<E>; falls through to a generic Set<E> for raw / unparameterized declarations
EnumMap<E,V>JSON object keyed by enum nameparses back to EnumMap for declared parameter EnumMap<E,V>; falls through to a generic Map<E,V> for raw / unparameterized declarations
BitSetset-bit-index list string (bitSetFormat: INDICES / BITS / HEX)first-class direct support — no swap; round-trips via BitSetFormat
Collection<E> / List<E> / Set<E> / Queue<E> / Deque<E>JSON arrayconcrete implementation chosen by declared type
Map<K,V>JSON objectconcrete implementation chosen by declared type
Iterator<E> / Iterable<E> / Stream<E>JSON array (serialize only)parse not supported

Juneau vs Jackson datatype modules​

Jackson ships per-type "datatype modules" (jackson-datatype-jsr310, jackson-datatype-jdk8, jackson-datatype-guava, …) that you opt into individually. Juneau collapses that surface in two ways:

  1. JDK datatypes (java.time, Optional*, BitSet, etc.) are supported in-box by direct serializer/parser code paths plus typed *Format enums for the format choice — no module to register.
  2. Third-party datatypes continue to follow the Swap / @Swap machinery, which is symmetric to a Jackson custom JsonSerializer/JsonDeserializer pair but additionally usable across every Juneau format.

The net result: most Spring-Boot-style "I need java.time in my JSON" wiring is unnecessary in Juneau — JsonSerializer.DEFAULT.write(Instant.now()) Just Works.


See also​