<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-marshall</artifactId>
<version>8.0.0</version>
</dependency>
juneau-marshall-8.0.0.jar
org.apache.juneau.marshall_8.0.0.jar
The juneau-marshall
library includes easy-to-use and highly customizable serializers and parsers
based around a common API.
They provide support for the following languages:
- JSON
- XML
- HTML
- UON (URL-Encoded Object Notation)
- URL-Encoding
- MessagePack
- SOAP/XML
- CSV
- OpenAPI (work-in-progress)
- BSON (coming soon)
- YAML (coming soon)
- Protobuf (coming soon)
- Amazon Ion (coming soon)
Features:
- Serializers can send output directly to Writers, OutputStreams, Files, Strings, or byte arrays.
- Parsers can receive input directly from Readers, InputStreams, Files, Strings, or byte arrays.
- Parsers can reconstruct arbitrarily complex data structures consisting of maps, collections, beans, and other POJOs.
- Serializers and parsers do not use intermediate DOMs! POJOs are serialized directly to streams and parsed back directly to POJOs, making them extremely efficient and fast.
- Supported languages are highly-customizable and powerful. For example, JSON support includes:
- Support for variants such as LAX syntax (unquoted attributes and single quotes).
- Support for embedded Javascript comments.
- Fully RFC1759 compliant.
- 20% faster than Jackson.
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;
}
Person p = new Person(); // An arbitrary POJO
// Produces:
// "{name:'John Smith',age:21}"
String laxJson = SimpleJsonSerializer.DEFAULT.serialize(p);
// Produces:
// "{"name":"John Smith","age":21}"
String strictJson = JsonSerializer.DEFAULT.serialize(p);
// Produces:
// <object>
// <name>John Smith</name>
// <age>21</age>
// </object>
String xml = XmlSerializer.DEFAULT.serialize(p);
// Produces:
// <table>
// <tr><td>name</td><td>John Smith</td></tr>
// <tr><td>age</td><td>21</td></tr>
// </table>
String html = HtmlSerializer.DEFAULT.serialize(p);
// Same as Html, but wraps it in HTML and BODY elements with page title/description/links:
String htmlDoc = HtmlDocSerializer.DEFAULT.serialize(p);
// Produces:
// name='John+Smith'&age=21
String urlEncoding = UrlEncodingSerializer.DEFAULT.serialize(p);
// Produces:
// (name='John Smith',age=21)
String uon = UonSerializer.DEFAULT.serialize(p);
// Produces:
// 82 A4 name AA 4A John Smith 68 A3 age 15
byte[] messagePack = MsgPackSerializer.DEFAULT.serialize(p);
More Information:
Parsing back into POJOs is equally simple for any of the supported languages shown above.
Language fragments are also supported.
JSON parsing shown here:
// Use one of the predefined parsers.
ReaderParser parser = JsonParser.DEFAULT;
// Parse a JSON object (creates a generic ObjectMap).
String json = "{name:'John Smith',age:21}";
Map m1 = parser.parse(json, Map.class);
// Parse a JSON string.
json = "'foobar'";
String s2 = parser.parse(json, String.class);
// Parse a JSON number as a Long or Float.
json = "123";
Long l3 = parser.parse(json, Long.class);
Float f3 = parser.parse(json, Float.class);
// Parse a JSON object as a bean.
json = "{name:'John Smith',age:21}";
Person p4 = parser.parse(json, Person.class);
// Parse a JSON object as a HashMap<String,Person>.
json = "{a:{name:'John Smith',age:21},b:{name:'Joe Smith',age:42}}";
Map<String,Person> m5 = parser.parse(json, HashMap.class, String.class, Person.class);
// Parse a JSON object as a HashMap<String,LinkedList<Person>>.
json = "{a:[{name:'John Smith',age:21},{name:'Joe Smith',age:42}]}";
Map<String,List<Person>> m6 = parser.parse(json, HashMap.class, String.class, LinkedList.class, Person.class);
// Parse a JSON array of integers as a Collection of Integers or int[] array.
json = "[1,2,3]";
List<Integer> l7 = parser.parse(json, LinkedList.class, Integer.class);
int[] i7 = parser.parse(json, int[].class);
// Parse arbitrary input into ObjectMap or ObjectList objects
// (similar to JSONObject/JSONArray but generalized for all languages).
json = "{name:'John Smith',age:21}";
ObjectMap m8a = parser.parse(json, ObjectMap.class);
int age = m8a.getInt("age");
ObjectMap m8b = (ObjectMap)parser.parse(json, Object.class); // Equivalent.
json = "[1,true,null]";
ObjectList l9a = parser.parse(json, ObjectList.class);
boolean b = l9a.getBoolean(1);
ObjectList l9b = (ObjectList)parser.parse(json, Object.class); // Equivalent.
More Information:
Marshalls are pairings of serializers and parsers in a single class for even simpler code:
Person p = new Person(); // An arbitrary POJO
// Serialize
String json = Json.DEFAULT.write(p);
String simpleJson = SimpleJson.DEFAULT.write(p);
String xml = Xml.DEFAULT.write(p);
String html = Html.DEFAULT.write(p);
String uon = Uon.DEFAULT.write(p);
String urlEncoding = UrlEncoding.DEFAULT.write(p);
String openapi = OpenApi.DEFAULT.write(p);
byte[] msgPack = MsgPack.DEFAULT.write(p);
String rdfXml = RdfXml.DEFAULT.write(p);
String rdfXmlAbbrev = RdfXmlAbbrev.DEFAULT.write(p);
String n3 = N3.DEFAULT.write(p);
String nTuple = NTuple.DEFAULT.write(p);
String turtle = Turtle.DEFAULT.write(p);
// Parse
p = Json.DEFAULT.read(json, Person.class);
p = Xml.DEFAULT.read(xml, Person.class);
p = Html.DEFAULT.read(html, Person.class);
p = Uon.DEFAULT.read(uon, Person.class);
p = UrlEncoding.DEFAULT.read(urlEncoding, Person.class);
p = OpenApi.DEFAULT.read(openapi, Person.class);
p = MsgPack.DEFAULT.read(msgPack, Person.class);
p = RdfXml.DEFAULT.read(rdfXml, Person.class);
p = RdfXmlAbbrev.DEFAULT.read(rdfXmlAbbrev, Person.class);
p = N3.DEFAULT.read(n3, Person.class);
p = NTuple.DEFAULT.read(nTuple, Person.class);
p = Turtle.DEFAULT.read(turtle, Person.class);
More Information:
Serializers and parsers are builder-based. Build from scratch or clone existing instances. Lots of configuration options available for all the languages.
// Create a serializer from scratch programmatically using a builder.
JsonSerializer serializer = JsonSerializer.create()
.simple() // Simple mode
.sq() // Use single quotes
.pojoSwaps( // 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
)
.beanFilters(MyBeanFilter.class) // Control how bean properties are handled
.timeZone(TimeZone.GMT) // For serializing Calendars
.locale(Locale.JAPAN) // For timezone-specific serialization
.sortCollections() // For locale-specific serialization
.sortProperties() // Various behavior settings
.trimNullProperties()
.trimStrings()
.methodVisibility(PROTECTED) // Control which fields/methods are serialized
.beanDictionary( // Adds type variables for resolution during parsing
MyBeanA.class,
MyBeanB.class
)
.debug() // Debug mode
.build();
// Same as above, but using declarative named properties.
// This is how serializers and parsers are typically configured on REST servlets and clients.
JsonSerializer serializer = JsonSerializer.create()
.set(JSON_simpleMode, true)
.set(SERIALIZER_quoteChar, '\'')
.set(BEAN_pojoSwaps_add, IteratorSwap.class)
.set(BEAN_pojoSwaps_add, ByteArrayBase64Swap.class)
.set(BEAN_pojoSwaps_add, CalendarSwap.ISO8601DT.class)
.set(BEAN_beanFilters_add, MyBeanFilter.class)
.set(BEAN_timeZone, TimeZone.GMT)
.set(BEAN_locale, Locale.JAPAN)
.set(SERIALIZER_sortCollections, true)
.set(BEAN_sortProperties, true)
.set(SERIALIZER_trimNullProperties, true)
.set(SERIALIZER_trimStrings, true)
.set(BEAN_methodVisibility, PROTECTED)
.set(BEAN_beanDictionary_add, MyBeanA.class)
.set(BEAN_beanDictionary_add, MyBeanB.class)
.set(BEAN_debug, true)
.build();
// Clone an existing serializer and modify it to use single-quotes.
JsonSerializer serializer = JsonSerializer.DEFAULT.builder()
.sq()
.build();
More Information:
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.
- Transforms
- Bean filters - Control how bean properties are handled (naming conventions, ordering, visibility,...).
- POJO swaps - Replace non-serializable POJOs with serializable equivalents.
Predefined swaps provided for common cases: ByteArrayBase64Swap
, 50+ variants of Calendar/Date swaps, Enumeration/Iterator
swaps.
- Annotations
Various annotations available for your POJO classes that are recognized by ALL serializers and parsers:
@Bean, @Pojo, @BeanIgnore, @BeanParam, @BeanProperty, @NameProperty, @ParentProperty
Annotations also provided for language-specific behaviors where it makes sense:
@Json, @Html, @Xml, @UrlEncoding
All annotations have programmatic equivalents when you don't have access to POJO source.
- Swap methods
By default, various instance and static methods and constructors are automatically detected and supported:
valueOf(String)
, parse(String)
, parseString(String)
, forName(String)
, forString(String)
,
fromString(String)
, T(String)
, Object swap(BeanSession)
, T unswap(BeanSession, T.class)
More Information:
UON (URL-Encoded Object Notation) 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
)
)
)
More Information:
Lots of shortcuts are provided throughout the API to simplify tasks, and the APIs are often useful for debugging and logging purposes as well:
// Create JSON strings from scratch using fluent-style code.
String jsonObject = new ObjectMap().append("foo","bar").toString();
String jsonArray = new ObjectList().append("foo").append(123).append(null).toString();
// Create maps and beans directly from JSON.
Map<String,Object> myMap = new ObjectMap("{foo:'bar'}");
List<Object> myList = new ObjectList("['foo',123,null]");
// Load a POJO from a JSON file.
MyPojo myPojo = JsonParser.DEFAULT.parse(new File("myPojo.json"));
// Serialize POJOs and ignore exceptions (great for logging)
String json = SimpleJson.DEFAULT.toString(myPojo);
// Dump a POJO to the console.
SimpleJson.DEFAULT.println(myPojo);
// Delayed serialization.
// (e.g. don't serialize an object if it's not going to be logged).
logger.log(FINE, "My POJO was: {0}", new StringObject(myPojo));
logger.log(FINE, "My POJO in XML was: {0}", new StringObject(XmlSerializer.DEFAULT, myPojo));
String message = new StringMessage("My POJO in {0}: {1}", "JSON", new StringObject(myPojo)).toString();
// Create a 'REST-like' wrapper around a POJO.
// Allows you to manipulate POJO trees using URIs and GET/PUT/POST/DELETE commands.
PojoRest pojoRest = new PojoRest(myPojo);
pojoRest.get(String.class, "addressBook/0/name");
pojoRest.put("addressBook/0/name", "John Smith");
More Information:
SerializerGroup
and ParserGroup
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.
SerializerGroup sg = SerializerGroup.create()
.append(JsonSerializer.class, UrlEncodingSerializer.class);
.ws() // or .setUseWhitespace(true) or .set(SERIALIZER_useWhitespace, true)
.pojoSwaps(CalendarSwap.ISO8601DT.class) // or .set(BEAN_pojoSwaps_add, CalendarSwap.ISO8601DT.class)
.build();
// Find the appropriate serializer by Accept type and serialize our POJO to the specified writer.
// Fully RFC2616 compliant.
sg.getSerializer("text/invalid, text/json;q=0.8, text/*;q:0.6, *\/*;q=0.0")
.serialize(myPersonObject, myWriter);
// Construct a new parser group with configuration parameters that get applied to all parsers.
ParserGroup pg = ParserGroup.create()
.append(JsonParser.class, UrlEncodingParser.class);
.pojoSwaps(CalendarSwap.ISO8601DT.class) // or .set(BEAN_pojoSwaps_add, CalendarSwap.ISO8601DT.class)
.build();
Person p = pg.getParser("text/json").parse(myReader, Person.class);
More Information: