Skip to main content

OpenAPI Serializers

The OpenApiSerializer class is used to convert POJOs to HTTP parts.

Class hierarchy​

The class hierarchy for the builder of this serializer is:

Context.BuilderMarshallingContextable.BuilderMarshallingTraverseContext.BuilderSerializer.BuilderWriterSerializer.BuilderUonSerializer.BuilderOpenApiSerializer.Builder

Refer to the builder javadocs for configurable settings.

Defining a schema​

Later we'll describe how to use HTTP-Part annotations to define OpenAPI schemas for serialization and parsing of HTTP parts.

The following example is a preview showing an HTTP body defined as pipe-delimited list of comma-delimited numbers (e.g. "1,2,3|4,5,6|7,8,9"):

@RestPost("/2dLongArray")
public void post2dLongArray(
@Content(
schema=@Schema(
type="array",
collectionFormat="pipes",
items=@Items(
type="array",
collectionFormat="csv",
items=@SubItems(
type="integer",
format="int64",
minimum="0",
maximum="100",
minLength=1,
maxLength=10
)
),
minLength=1,
maxLength=10
)
)
Long[][] body
) {...}

// Alternate shortened format.
@RestPost("/2dLongArray")
public void post2dLongArray(
@Content(
schema=@Schema(
t="array",
cf="pipes",
items=@Items(
t="array",
cf="csv",
items=@SubItems(
t="integer",
f="int64",
min="0",
max="100",
minl=1,
maxl=10
)
),
minl=1,
maxl=10
)
)
Long[][] body
) {...}

Under-the-covers, this gets converted to the following schema object:

HttpPartSchema schema = HttpPartSchema.create()
.items(
HttpPartSchema.create()
.type("array")
.collectionFormat("pipes")
.items(
HttpPartSchema.create()
.type("array")
.collectionFormat("csv")
.items(
HttpPartSchema.create()
.type("integer")
.format("int64")
.minimum(0)
.maximum(100)
)
)
)
.build();

Various convenience methods exist for shortening this code.

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

HttpPartSchema schema = tArrayPipes(
tArrayCsv(
tInt64().minimum(0).maximum(100)
)
).build();

The following program uses the schema above to create our pipe+csv list of numbers:

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

import org.apache.juneau.commons.httppart.*;
import org.apache.juneau.marshall.httppart.*;
import org.apache.juneau.marshall.oapi.*;

public class PipeCsvSerializeExample {

public static void main(String[] args) throws Exception {
HttpPartSchema schema = tArrayPipes(
tArrayCsv(
tInt64().min(0).max(100)
)
).build();

// Our POJO being serialized.
Long[][] input = { {1L, 2L, 3L}, {4L, 5L, 6L}, {7L, 8L, 9L} };

// Convert POJO to a string.
try {
String httpPart = OpenApiSerializer.DEFAULT.write(HttpPartType.QUERY, schema, input);
System.out.println(httpPart);
} catch (SchemaValidationException e) {
// Oops, one of the restrictions were not met.
System.out.println("rejected: " + e.getMessage());
}
}
}

The program prints 1,2,3|4,5,6|7,8,9.

Allowed POJO types​

As a general rule, any POJO convertible to the intermediate type for the type/format of the schema can be serialized using the OpenAPI serializer.

Here are the rules of POJO types allowed for various type/format combinations:

TypeFormatValid parameter types
string or emptybyte
binary
binary-spaced

• byte[] (default)
• InputStream
• Reader - Read into String and then converted using String.getBytes()
• Object - Converted to String and then converted using String.getBytes()
• Any POJO transformable to a byte[] via:

  • public byte[] toBytes() {...}
  • public byte[] toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
date
date-time

• Calendar (default)
• Date
• Any POJO transformable to a Calendar via:

  • public Calendar toCalendar() {...}
  • public Calendar toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
uon• Any Serializable POJO type
empty

• String (default)
• Any POJO transformable to a String via:

  • public String toString() {...}
  • Any POJO transformable via an ObjectSwap
booleanempty

• Boolean (default)
• boolean
• String - Converted to a Boolean
• Any POJO transformable to a Boolean via:

  • public Boolean toBoolean() {...}
  • public Boolean toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
integerint32

• Integer (default)
• int
• String - Converted to an Integer
• Any POJO transformable to an Integer via:

  • public Integer toInteger() {...}
  • public Integer toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
int64

• Long (default)
• long
• String - Converted to a Long
• Any POJO transformable to a Long via:

  • public Long toLong() {...}
  • public Long toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
numberfloat

• Float (default)
• float
• String - Converted to a Float
• Any POJO transformable to a Float via:

  • public Float toFloat() {...}
  • public Float toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
double

• Double (default)
• double
• String - Converted to a Double
• Any POJO transformable to a Double via:

  • public Double toDouble() {...}
  • public Double toFoo() {...} (any method starting with "to")
  • Any POJO transformable via an ObjectSwap
arrayempty

• Arrays or Collections of any defaults on this list
• Any POJO transformable to arrays of the default types (e.g. Integer[], Boolean[][], etc...)
For example: public Boolean[][] toBooleanArrayArray() {...} (the method name is to followed by the readable type name, e.g. toLongArray() for Long[])
• Any POJO transformable to arrays of the default types via an ObjectSwap

uon• Any Serializable POJO type
objectempty

• Map<String,Object> (default)
• Beans with properties of anything on this list
• Any POJO transformable to a map via an ObjectSwap

uon• Any Serializable POJO type

For arrays, an example of "Any POJO transformable to arrays of the default types" is:

// Sample POJO class convertable to a Long[][].
public class MyPojo {

// toX method used by serializer.
public Long[][] toLongArrayArray() {...}
}

The method name must be to followed by the readable name of the target type (matched case-insensitively), which for Long[][] is toLongArrayArray.

In the example above, our POJO class can be used to create our pipe-delimited list of comma-delimited numbers:

// Our POJO being serialized.
MyPojo input = ....

// Convert POJO to a string.
try {
String httpPart = OpenApiSerializer.DEFAULT.write(HttpPartType.QUERY, schema, input);
} catch (SchemaValidationException e) {
// Oops, one of the restrictions were not met.
}

For a MyPojo whose toLongArrayArray() returns {{1,2},{3,4}} and a schema of tArrayPipes(tArrayCsv(tInt64())), the serialized output is:

1,2|3,4

Object types​

The object type is not officially part of the OpenAPI standard. However, Juneau supports serializing Maps and beans to HTTP parts as comma-delimited name=value pairs. The following shows an example of a bean with several properties of various types.

import java.util.*;

public class MyBean {
private static byte[] FOOB = "foo".getBytes();

public String f1 = "foo";
public byte[] f2 = FOOB;
public byte[] f3 = FOOB;
public byte[] f4 = FOOB;
public Calendar f5 = new GregorianCalendar(2012, Calendar.DECEMBER, 21, 12, 34, 56);
public String f6 = "foo";
public int f7 = 1;
public Long f8 = 2l;
public float f9 = 1.0f;
public Double f10 = 1.0;
public Boolean f11 = true;
public Object fExtra = "1";
}

We define the following schema:

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

HttpPartSchema schema = tObject()
.p("f1", tString())
.p("f2", tByte())
.p("f3", tBinary())
.p("f4", tBinarySpaced())
.p("f5", tDateTime())
.p("f6", tUon())
.p("f7", tInteger())
.p("f8", tInt64())
.p("f9", tNumber())
.p("f10", tDouble())
.p("f11", tBoolean())
.ap(tInteger())
.build();

Then we serialize our bean. The complete program:

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

import java.util.*;

import org.apache.juneau.commons.httppart.*;
import org.apache.juneau.marshall.httppart.*;
import org.apache.juneau.marshall.oapi.*;

public class MyBeanSerializeExample {

public static class MyBean {
private static byte[] FOOB = "foo".getBytes();

public String f1 = "foo";
public byte[] f2 = FOOB;
public byte[] f3 = FOOB;
public byte[] f4 = FOOB;
public Calendar f5 = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US);
public String f6 = "foo";
public int f7 = 1;
public Long f8 = 2l;
public float f9 = 1.0f;
public Double f10 = 1.0;
public Boolean f11 = true;
public Object fExtra = "1";

public MyBean() {
f5.clear();
f5.set(2012, Calendar.DECEMBER, 21, 12, 34, 56);
}
}

public static void main(String[] args) throws Exception {
HttpPartSchema schema = tObject()
.p("f1", tString())
.p("f2", tByte())
.p("f3", tBinary())
.p("f4", tBinarySpaced())
.p("f5", tDateTime())
.p("f6", tUon())
.p("f7", tInteger())
.p("f8", tInt64())
.p("f9", tNumber())
.p("f10", tDouble())
.p("f11", tBoolean())
.ap(tInteger())
.build();

String httpPart = OpenApiSerializer.DEFAULT.write(HttpPartType.QUERY, schema, new MyBean());
System.out.println(httpPart);
}
}

The results of this serialization is shown below. Properties are written in alphabetical order:

f1=foo,f10=1.0,f11=true,f2=Zm9v,f3=666F6F,f4=66 6F 6F,f5=2012-12-21T12:34:56Z,f6=foo,f7=1,f8=2,f9=1.0,fExtra=1

Beans with array properties​

The following is an example of a bean with various array property types:

import java.util.*;

public class MyBean {
private static byte[] FOOB = "foo".getBytes();

public String[] f1 = {"a,b",null};
public byte[][] f2 = new byte[][]{FOOB,null};
public byte[][] f3 = new byte[][]{FOOB,null};
public byte[][] f4 = new byte[][]{FOOB,null};
public Calendar[] f5 = new Calendar[]{new GregorianCalendar(2012, Calendar.DECEMBER, 21, 12, 34, 56),null};
public String[] f6 = {"a","b",null};
public Integer[] f7 = new Integer[]{1,2,null};
public Integer[] f8 = new Integer[]{3,4,null};
public Float[] f9 = new Float[]{1f,2f,null};
public Float[] f10 = new Float[]{3f,4f,null};
public Boolean[] f11 = new Boolean[]{true,false,null};
public Object[] fExtra = new Object[]{1,"2",null};
}

For this bean, we define the following schema:

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

HttpPartSchema schema = tObject()
.p("f1", tArray(tString()))
.p("f2", tArray(tByte()))
.p("f3", tArray(tBinary()))
.p("f4", tArray(tBinarySpaced()))
.p("f5", tArray(tDateTime()))
.p("f6", tArray(tUon()))
.p("f7", tArray(tInteger()))
.p("f8", tArray(tInt64()))
.p("f9", tArray(tNumber()))
.p("f10", tArray(tDouble()))
.p("f11", tArray(tBoolean()))
.ap(tArray(tInteger()))
.build();

The following program serializes this bean. Items within an array property are comma-delimited, and the commas are escaped by a backslash so they survive inside the enclosing name=value list:

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

import java.util.*;

import org.apache.juneau.commons.httppart.*;
import org.apache.juneau.marshall.httppart.*;
import org.apache.juneau.marshall.oapi.*;

public class MyArrayBeanSerializeExample {

public static class MyBean {
private static byte[] FOOB = "foo".getBytes();

private static Calendar utc(int y, int m, int d, int h, int mi, int sec) {
var c = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US);
c.clear();
c.set(y, m, d, h, mi, sec);
return c;
}

public String[] f1 = {"a,b",null};
public byte[][] f2 = new byte[][]{FOOB,null};
public byte[][] f3 = new byte[][]{FOOB,null};
public byte[][] f4 = new byte[][]{FOOB,null};
public Calendar[] f5 = new Calendar[]{utc(2012, Calendar.DECEMBER, 21, 12, 34, 56),null};
public String[] f6 = {"a","b",null};
public Integer[] f7 = new Integer[]{1,2,null};
public Integer[] f8 = new Integer[]{3,4,null};
public Float[] f9 = new Float[]{1f,2f,null};
public Float[] f10 = new Float[]{3f,4f,null};
public Boolean[] f11 = new Boolean[]{true,false,null};
public Object[] fExtra = new Object[]{1,"2",null};
}

public static void main(String[] args) throws Exception {
HttpPartSchema schema = tObject()
.p("f1", tArray(tString()))
.p("f2", tArray(tByte()))
.p("f3", tArray(tBinary()))
.p("f4", tArray(tBinarySpaced()))
.p("f5", tArray(tDateTime()))
.p("f6", tArray(tUon()))
.p("f7", tArray(tInteger()))
.p("f8", tArray(tInt64()))
.p("f9", tArray(tNumber()))
.p("f10", tArray(tDouble()))
.p("f11", tArray(tBoolean()))
.ap(tArray(tInteger()))
.build();

System.out.println(OpenApiSerializer.DEFAULT.write(HttpPartType.QUERY, schema, new MyBean()));
}
}

The output is:

f1=a\\\,b\,null,f10=3.0\,4.0\,null,f11=true\,false\,null,f2=Zm9v\,null,f3=666F6F\,null,f4=66 6F 6F\,null,f5=2012-12-21T12:34:56Z\,null,f6=a\,b\,null,f7=1\,2\,null,f8=3\,4\,null,f9=1.0\,2.0\,null,fExtra=1\,2\,null
note
  • Array properties can also use CSV/SSV/PIPES for array notation.
  • Various notations can be mixed throughout.
  • Schemas and POJOs can be defined arbitrarily deep.
  • Schemas are optional. They can be skipped or partially defined.

We make our best attempt to convert the input to the matching type. However, you will get SerializeExceptions if you attempt an impossible conversion. (e.g. trying to serialize the string "foo" as a boolean).