{title:'Core Overview', created:'9.0.0'}
Marshalling APIs

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:

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 = JsonSerializer.DEFAULT.serialize(new Person());

Parsing back into POJOs is equally simple for any of the supported languages Language fragments are also supported.

| // Parse a JSON object as a bean. | String json = "{\"name\":\"John Smith\","\age\":21}"; | Person person = parser.parse(json, Person.class);

Marshalls are pairings of serializers and parsers in a single class for even simpler code:

| // Serialize | String json = Json.DEFAULT.write(person); | | // Parse | Person person = Json.DEFAULT.read(json, Person.class);

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

Marshaller Builders

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 = JsonSerializer | .create() | .simple() // Simple mode | .sq() // Use single quotes | .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();

Many POJOs such as primitives, beans, collections, arrays, and classes with various known constructors and methods are serializable out-of-the-box. For other objects, "transforms" allow you to perform various mutations on them before serialization and after parsing.

{@doc jm.Swaps Object swaps} allow you to replace non-serializable objects with serializable equivalents. The {@link oaj.swaps} package contains a variety of predefined swaps.

| // Create a serializer from scratch programmatically using a builder. | JsonSerializer serializer = JsonSerializer | .create() | .swaps( // Swap unserializable classes with surrogate POJOs | IteratorSwap.class, // Iterators swapped with lists | ByteArrayBase64Swap.class, // byte[] swapped with base-64 encoded strings | CalendarSwap.ISO8601DT.class // Calendars swapped with ISO8601-compliant strings | ) | .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:

| // Sort bean properties by name. | // Exclude city/state from marshalling. | @Bean(sort=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.

Configuration Annotations

Serializers and parsers can also be configured using annotations.

| @BeanConfig(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.

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

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

Default values for configuration settings can be overridden via system properties or environment variables. For example, the system property "BeanContext.sortProperties" or environment variable "BEANCONTEXT_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 annototations like so:

| @Bean(onClass=Address.class, sort=true, excludeProperties="city,state") | public class MyAnnotatedClass {...}

Simplified JSON Marshalling

The {@link oaj.json.SimpleJsonSerializer} class can be used to serialized POJOs into Simplified JSON notation.

Simplified JSON is identical to JSON except for the following:

Examples:

| // Some free-form JSON. | Map map = JsonMap.of( | "foo", "x1", | "_bar", "x2", | " baz ", "x3", | "123", "x4", | "return", "x5", | "", "x6" | );

| // Serialized to standard JSON | { | "foo": "x1", | "_bar": "x2", | " baz ": "x3", | "123": "x4", | "return": "x5", | "": "x6" | }

// Serialized to simplified JSON | { | foo: 'x1', | _bar: 'x2', | ' baz ': 'x3', // Quoted due to embedded spaces. | '123': 'x4', // Quoted to prevent confusion with number. | 'return': 'x5', // Quoted because it's a keyword. | '': 'x6' // Quoted because it's an empty string. | }

Simplified JSON is still valid Javascript. The advantage to simplified JSON is you can represent it in a Java String in minimal form with minimal escaping. This is particularly useful in cases such as unit testing where you can easily validate POJOs by simplifying them to Simplified JSON and do a simple string comparison.

| WriterSerializer serializer = SimpleJsonSerializer.DEFAULT; | assertEquals("{foo:'bar',baz:123}", serializer.toString(myPojo));

UON Marshalling

The Marshalling API also supports UON (URL-Encoded Object Notation). It allows JSON-like data structures (OBJECT, ARRAY, NUMBER, BOOLEAN, STRING, NULL) in HTTP constructs (query parameters, form parameters, headers, URL parts) without violating RFC2396. This allows POJOs to be converted directly into these HTTP constructs which is not possible in other languages such as JSON.

| ( | id=1, | name='John+Smith', | uri=http://sample/addressBook/person/1, | addressBookUri=http://sample/addressBook, | birthDate=1946-08-12T00:00:00Z, | addresses=@( | ( | uri=http://sample/addressBook/address/1, | personUri=http://sample/addressBook/person/1, | id=1, | street='100+Main+Street', | city=Anywhereville, | state=NY, | zip=12345, | isCurrent=true | ) | ) | )

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.httpart.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.serialize(HttpPartType.HEADER, 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", min=1, max=32) int myParam, | @Header("MyHeader", pattern="foo.*") String p2 | ) {...}

JsonMap/JsonList

The {@link oaj.collections.JsonMap} and {@link oaj.collections.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 = JsonMap.create().append("foo","bar").toString(); | String myList = JsonList.of("foo", 123, null, jsonObject).toString(); | | // Parse directly from JSON into generic DOMs. | Map<String,Object> myMap = JsonMap.ofJson("{foo:'bar'}"); | List<Object> myList = JsonList.ofJson("['foo',123,null]");

These classes provide lots of convenience methods including:

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); | .forEach(x -> x.swaps(CalendarSwap.ISO8601DT.class)) | .forEachWS(x -> x.useWhitespace()) | .build(); | | // Find the appropriate serializer by Accept type and serialize our POJO to the specified writer. | // Fully RFC2616 compliant. | serializerSet | .getSerializer("text/invalid, text/json;q=0.8, text/*;q:0.6, *\/*;q=0.0") | .serialize(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); | .forEach(x -> x.swaps(CalendarSwap.ISO8601DT.class)) | .build(); | | Person person = parserSet | .getParser("text/json") | .parse(myReader, Person.class);

SVL Variables

The {@link oaj.svl} package defines an API for a language called "Simple Variable Language". In a nutshell, Simple Variable Language (or SVL) is text that contains variables of the form "$varName{varKey}".

Variables can be recursively nested within the varKey (e.g. "$FOO{$BAR{xxx},$BAZ{xxx}}"). Variables can also return values that themselves contain more variables.

| // Use the default variable resolver to resolve a string that | // contains $S (system property) variables | String myProperty = VarResolver.DEFAULT.resolve("The Java home directory is $S{java.home}");

The SVL variables are used widely throughout various annotations defined in Juneau allowing many features to be configured via external sources such as configuration files or environment variables/system properties. The SVL APIs are extensible allowing for the addition of new types of variables.

Assertions API

The {@link oaj.assertions} package in Juneau is a powerful API for performing fluent style assertions.

Examples:

| import static org.apache.juneau.assertions.Assertions.*; | import static org.apache.juneau.assertions.AssertionPredicates.*; | | // Check the contents of a string. | assertString("foo, bar") | .split(",") | .trim() | .has("foo", "bar"); | | // Extract a subset of properties from a list of beans and compare using Simplified JSON. | List<MyBean> myListOfBeans = ...; | assertBeanList(myListOfBeans) | .extract("a,b") | .asJson().is("[{a:1,b:'foo'}]"); | | // Perform an arbitrary Predicate check against a bean. | MyBean myBean = ...; | assertBean(myBean) | .is(x -> isValidCheck(x)) | | // Check that a list of strings has less than 10 entries and the first | // 3 entries are [foo, bar*, null] using assertion predicates. | List<String> myListOfStrings = ...; | assertStringList(myListOfStrings) | .size().isLt(10) | .first(3) | .each(eq("foo"),match("bar*"),isNull()) | | // Check that an exception is thrown and is the specified type and has the specified message. | assertThrown(()->myBean.runBadMethod()) | .exists() | .isExactType(RuntimeException.class) | .message().is("foo");

The Assertions APIs are used throughout the REST client and server APIs for performing inline assertions on REST requests and responses.

Example:

| // Create a basic REST client with JSON support and download a bean. | MyBean bean = RestClient.create() | .simpleJson() | .build() | .get(URI) | .run() | .assertStatus().asCode().is(200) | .assertHeader("Content-Type").isMatches("application/json*") | .getContent().assertValue().asString().isContains("OK") | .getContent().as(MyBean.class);