Skip to main content

juneau-marshall

juneau.version is 10.0.0-SNAPSHOT for these docs (the latest release on Maven Central is 9.2.0); see Install Juneau for the property and the snapshots repository.

Maven
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-marshall</artifactId>
<version>${juneau.version}</version>
</dependency>
Jarjuneau-marshall-10.0.0.jar
OSGiorg.apache.juneau.marshall_10.0.0.jar
LinksMaven Central · Javadocs · Source

Contents/Features​

  • Foundation for all serializers and parsers.
  • Implementations for all serializers and parsers except RDF languages.
  • Various reusable utilities used throughout the framework.

Overview​

The juneau-marshall library includes easy-to-use and highly customizable serializers and parsers based around a common API. It allows you to marshall Java POJOs directly to and from a wide variety of language types without the need for intermediate Document Object Models making them extremely efficient.

Supported languages include:

These are the 38 formats the docs count; see Counting formats.


The default serializers can often be used to serialize POJOs in a single line of code:

// A simple bean
public class Person {
public String name = "John Smith";
public int age = 21;
}

// Produces:
// "{"name":"John Smith","age":21}"
String json = Json.of(new Person());

Parsing back into POJOs is equally simple for any of the supported languages.

Language fragments are also supported.

// Parse a JSON5 fragment (unquoted keys, single-quoted values) as a bean.
String json5 = "{name:'John Smith',age:21}";
Person person = Json5.to(json5, Person.class);

Marshalling support is provided for a wide variety of POJO types including:

  • Primitives and primitive wrappers (int, Integer, boolean, Boolean, etc.)
  • Java beans (standard getters/setters)
  • Java Records
  • Enums
  • Java Collections Framework objects (List, Set, Map, Queue, Deque, etc.)
  • Arrays (including multi-dimensional)
  • Optional and OptionalInt/OptionalLong/OptionalDouble
  • Date/time types (Calendar, Date, Temporal subtypes, Duration, Period)
  • Iterator and Stream (serialization only — consumed lazily without buffering)
  • Classes with toString()/fromString() or valueOf(String) methods
  • Classes with constructors taking a single String argument
  • Interfaces (when an implementation class is specified via @Marshalled(implClass=...))
  • Any other type via pluggable Swaps
See Also
  • Pojo Categories for an exhaustive list of categories supported out-of-the-box.

Counting formats​

These docs count 38 formats (26 in juneau-marshall, 12 RDF in juneau-marshall-rdf). One format is one marshaller class that both serializes and parses: a public class in the marshaller package of either module that extends CharMarshaller or StreamMarshaller, such as Json or Turtle. The 12 RDF formats live in juneau-marshall-rdf and need Apache Jena. SOAP is not counted: SoapXmlSerializer writes SOAP envelopes but has no parser. JsonPatchMarshaller (in juneau-bean-jsonpatch) is not counted either: it is JSON pre-wired for JSON Patch, not a separate format.

Token and Record Streaming​

For large datasets, the marshalling API supports O(1)-memory streaming via RecordReader and RecordWriter cursors. Records are produced or consumed one at a time without buffering the entire document. JSON5L (line-delimited JSON5) is a natural fit — each line is an independent record:

// Write a large dataset line-by-line to an OutputStream.
try (RecordWriter w = Json5l.DEFAULT.toRecords(myOutputStream)) {
for (Person p : millionsOfPeople)
w.write(p);
}

// Read it back record-by-record — constant memory regardless of dataset size.
try (RecordReader r = Json5l.DEFAULT.fromRecords(myInputStream)) {
Person p;
while ((p = r.read(Person.class)) != null)
process(p);
}

Formats like JSON and CBOR additionally expose a structural TokenReader/TokenWriter cursor that streams individual tokens (OBJECT_START, KEY, VALUE, OBJECT_END, …) for use cases such as schema validation, transformation pipelines, and custom serialization logic.

See Also

Serializer/Parser Builders​

Marshallers like the one shown above are pairings of serializers and parsers.

Serializers and parsers are builder-based using fluent methods allowing you to quickly create, clone, and modify them in single lines of code.

// Create a serializer from scratch programmatically using a builder.
JsonSerializer serializer = Json5Serializer // Simple mode, single quotes
.create()
.timeZone(TimeZone.GMT) // For timezone-specific serialization
.locale(Locale.JAPAN) // For locale-specific serialization
.sortCollections()
.sortProperties()
.keepNullProperties()
.trimStrings()
.beanMethodVisibility(PROTECTED) // Control which fields/methods are serialized
.beanDictionary( // Adds type variables for resolution during parsing
MyBeanA.class,
MyBeanB.class
)
.debug() // Debug mode
.build();

Swaps allow you to replace non-serializable objects with serializable equivalents or to override the default serialization format for types that already have built-in support. The org.apache.juneau.marshall.swaps package contains a variety of predefined swaps.

// Create a serializer from scratch programmatically using a builder.
JsonSerializer serializer = JsonSerializer
.create()
.binaryFormat(BinaryFormat.BASE64) // byte[] serialized as base-64 encoded strings
.calendarFormat(CalendarFormat.RFC_1123_DATE_TIME) // Override default ISO 8601 format for Calendars
.build();

Any POJO that doesn't fit into the category of a bean/collection/array/primitive and doesn't have a swap associated with it is converted to simple strings.

By default, various instance and static methods and constructors on POJO classes are automatically detected and supported for marshalling a POJO to and from a string.

Bean Annotations​

Beans and POJO classes, methods, fields, and constructors can also be annotated with a variety of annotations to customize how they are marshalled:

// Disable alphabetical sorting of bean properties (bean properties are sorted by default).
// Exclude city/state from marshalling.
@BeanType(unsorted=true, excludeProperties="city,state")
public class Address { ... }

// Specify an implementation class for an interface.
@Marshalled(implClass=AutomobileImpl.class)
public interface Automobile { ... }

As a general rule, any capabilities provided by bean annotations can be programmatically specified via the builder APIs. This allows the marshallers to be used equivalently on either your own code that you have access to, or external code where you only have access to binaries.

See Also

Configuration Annotations​

Serializers and parsers can also be configured using annotations.

@MarshalledConfig(sortProperties="true")
@SerializerConfig(quoteChar="'")
@RdfConfig(rdfxml_tab="5", addRootProperty="true")
public class MyAnnotatedClass {...}

// Create a serializer configured using annotations.
JsonSerializer serializer = JsonSerializer
.create()
.applyAnnotations(MyAnnotatedClass.class)
.build();

Config annotations are extensively used in the REST Servlet APIs to configure how POJOs are marshalled through REST interfaces.

Config variables also support embedded variables for resolving settings at runtime.

// Sort properties depending on value of system property "sortProperties".
@MarshalledConfig(sortProperties="$S{sortProperties,false}")

Default values for config settings can be overridden via system properties or environment variables. For example, the system property "MarshallingContext.sortProperties" or environment variable "MARSHALLINGCONTEXT_SORTPROPERTIES" can be used to set the default value for the sort properties setting.

Bean annotations can also be programmatically attached to POJOs using config annotations like so:

@BeanTypeApply(onClass=Address.class, value=@BeanType(unsorted=true, excludeProperties="city,state"))
public class MyAnnotatedClass {...}
See Also

OpenAPI Marshalling​

The Marshalling API also supports schema-based OpenAPI serialization. It allows HTTP parts to be marshalled to-and-from POJOs based on OpenAPI schema definitions.

import static org.apache.juneau.marshall.httppart.HttpPartSchema.*;

// Schema - Pipe-delimited list of comma-delimited longs.
HttpPartSchema schema = tArrayPipes().items(
tArrayCsv().items(
tInt64()
)
).build();

// Our value to serialize
Object value = new long[][]{{1,2,3},{4,5,6},{7,8,9}};

// Produces "1,2,3|4,5,6|7,8,9"
String output = OpenApiSerializer.DEFAULT.write(HttpPartType.QUERY, schema, value);

Schema-based serialization and parsing is used heavily in both the server and client REST APIs with built-in schema validations support in various HTTP part annotations.

// REST server method with HTTP parts using schema validation.
@RestGet
public void doGet(
@Query(name="myParam", schema=@Schema(min=1, max=32)) int myParam,
@Header("MyHeader", schema=@Schema(pattern="foo.*")) String p2
) {...}
See Also

Bean Property Schema Validation​

Parsers and serializers can also enforce JSON-Schema constraints declared via @Schema on bean properties, independent of HTTP parts. Validation is opt-in via MarshallingContext.Builder.validateSchema() and is powered by JsonSchemaValidator in juneau-bean-jsonschema.

public class MyBean {
@Schema(minLength=2, maxLength=10) public String name;
@Schema(minimum="0", maximum="150") public int age;
}

ReaderParser p = JsonParser.create().validateSchema().build();
p.read("{\"name\":\"a\",\"age\":42}", MyBean.class); // throws ParseException - minLength

The flag flows through @BeanConfig(validateSchema="true") and every builder subclass (parsers, serializers, REST clients). Failures surface as ParseException on the parse side and SerializeException on the serialize side. When juneau-bean-jsonschema is not on the classpath the flag becomes a silent no-op.

See Also

JsonMap/JsonList​

The JsonMap and JsonList collections classes allow you to programmatically build generic JSON data structures. They are similar in concept to JSONObject and JSONArray that you find in other JSON marshalling APIs but can be used to generate DOMs in any of the supported languages.

// Create JSON strings from scratch using fluent-style code.
String myMap = Json5Map.create().append("foo","bar").toJson5();
String myList = Json5List.of("foo", 123, null, jsonObject).toJson5();

// Parse directly from JSON5 into generic DOMs.
Map myMap = Json5Map.ofString("{foo:'bar'}");
List myList = Json5List.ofString("['foo',123,null]");

Both pairs sit on top of a new neutral MarshalledMap / MarshalledList base introduced in v10.0; JsonMap / JsonList carry the strict RFC 8259 JSON flavor, and Json5Map / Json5List carry the JSON5 flavor used in the examples above.

These classes provide lots of convenience methods including:

  • Methods for direct marshalling to/from any of the other supported languages.
  • Methods for quick conversions to other data types including collections, beans, arrays, etc...
See Also

Serializer and Parser Sets​

SerializerSet and ParserSet classes allow serializers and parsers to be retrieved by W3C-compliant HTTP Accept and Content-Type values:

// Construct a new serializer group with configuration parameters that get applied
// to all serializers.
SerializerSet serializerSet = SerializerSet
.create()
.add(JsonSerializer.class, UrlEncodingSerializer.class)
.forEachWS(x -> x.useWhitespace())
.build();

// Find the appropriate serializer by Accept type and serialize our POJO to the specified writer.
// Fully RFC2616 compliant. The lookup returns an Optional - a missing match on the
// Accept header maps to HTTP 406 (Not Acceptable).
serializerSet
.getSerializer("text/invalid, text/json;q=0.8, text/*;q:0.6, *\/*;q=0.0")
.orElseThrow(NotAcceptable::new) // HTTP 406 - no serializer matched the Accept header
.write(person, myWriter);

// Construct a new parser group with configuration parameters that get applied to all parsers.
ParserSet parserSet = ParserSet
.create()
.add(JsonParser.class, UrlEncodingParser.class);
.build();

// The lookup returns an Optional - a missing match on the Content-Type maps to
// HTTP 415 (Unsupported Media Type).
Person person = parserSet
.getParser("text/json")
.orElseThrow(UnsupportedMediaType::new) // HTTP 415 - no parser matched the Content-Type
.read(myReader, Person.class);
See Also

SVL Variables​

Juneau includes built-in support for Simple Variable Language (SVL) — a text-template engine that resolves variables of the form $varName{varKey} against sources such as system properties, environment variables, config files, and more. SVL is used widely throughout Juneau annotations and configuration settings.

See Simple Variable Language for the built-in variable reference, #{...} function-call syntax, compiled-template API, and more.

In This Section​