-
Notifications
You must be signed in to change notification settings - Fork 102
Boon JSON in five minutes
Boon Home | Boon Source | If you are new to boon, you might want to start here. Simple opinionated Java for the novice to expert level Java Programmer. Low Ceremony. High Productivity. A real boon to Java to developers!
Check out this tutorial on Boon. Boon Tutorial by the master tutorial write and my personal buddy
Boon is not a JSON parsing project. It is more than that, but JSON parsing is intrinsic to what Boon is all about. Boon is the probably the fastest way to serialize and parse JSON in Java so far for your project.. It is faster at object serialization, enabling JSON expressions, JSON parsing and much more. Boon JSON is FAST! In addition it has a very easy to use, convention-based API.
(Boon should be the fastest for most use cases that I have seen for REST, but there are some clear areas where Jackson and FASTJson are faster. At the moment, Boon is not the fastest way to encode JSON if you have a large strings with unicode outside of the ASCII range. The plan is to close the gap, but currently this makes Jackson faster for these use cases. Also please note that Boon is a newer project and not as mature as Jackson or GSON.)
Latest round of benchmarks to coincide with release
TOC
- Process Model For Data Binding and Serialization
- JSON full data binding
- Simple Data Binding - working with maps and lists
- Complete code listing so far
- Just works philosophy by example
- Boon and REST, HTTP GET POST
- Code Listing so far
- Working with Dates - smart standard date handling
- Raw data binding
- Data bindings with Generics
- Using Annotations to include or exclude properties
- Complete Code listing so far
- Primitive Parsing - parse primitive and primitive arrays
- Customizing JSON parser and serializer with factories
- Boon is faster than Jackson, GSON and others
- Filtering fields based on active view
- Working with subclasses, interfaces and abstract classes
- Changing the names of JSON properties using Annotations
Boon mainly focuses on data binding and index overlay. The final interface is always about data binding as it is the most convenient one to use. Data binding allows for conversion between JSON data and Java objects.
There is no public tree model in Boon JSON. The end users view is always a data binding view.
The ObjectMapper enables Data binding and is built on top of the JsonParser and JsonSerializer APIs, and mimics the Jackson API, which is the most popular Java JSON serializer/parser.
You can do simple data binding that only uses standard JDK container types (List, Maps) and scalar types (String, Boolean, Number, nulls). There is even a low-level API that can do wrapper-less (no auto-boxing) primitive arrays. You can also do full data binding that can use full range of Java types including "POJOs" (Plain Old Java Objects) such as Java Beans. Both can be used via the all-powerful ObjectMapper interface.
Serialization is achieved by:
Jackson style
ObjectMapper mapper = JsonFactory.create();
mapper.writeValue(dst, myBean); // where 'dst' can be File, OutputStream or Writer
Gson Style
ObjectMapper mapper = JsonFactory.create();
String dst = mapper.toJson(myBean);
When using full data binding, deserialization type must be fully specified as something other than Object.class. For example:
Jackson style
MyBean value = mapper.readValue(src, MyBean.class); // 'src' can be File, InputStream, Reader, String
Gson style
MyBean value = mapper.fromJson(src, MyBean.class); // 'src' can be File, InputStream, Reader, String
Boon supports both Jackson and Gson style annotations so if you are coming from those project or are used to those projects, Boon should just work.
The main complication is handling of Generic types: if they are used, one has to use the two argument version of readValue to work around Java Type Erasure:
List<MyBean> beans = mapper.readValue(src, List.class, MyBean.class);
As mentioned above, simple here just means that range of value types is limited to core JDK types. If this is acceptable, deserialization type can be simply defined as Object.class. This can apply at root level (for calls to ObjectMapper, as well as at lower level -- whatever value are declared to be of basic Object type will use Simple data binding. Simple like this:
Object root = mapper.readValue(src, Object.class);
Map<String,Object> rootAsMap = mapper.readValue(src, Map.class);
package org.boon.json;
import org.boon.Lists;
import java.io.File;
import java.util.List;
import java.util.Map;
import static org.boon.Boon.puts;
/**
* Created by rick on 1/4/14.
*/
public class JsonTutorial {
public static class MyBean {
String name = "Rick";
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
'}';
}
}
public static void main (String... args) throws Exception {
MyBean myBean = new MyBean();
File dst = File.createTempFile("emp", ".json");
ObjectMapper mapper = JsonFactory.create();
puts ("json string", mapper.writeValueAsString( myBean ));
mapper.writeValue( dst, myBean ); // where 'dst' can be File, OutputStream or Writer
File src = dst;
MyBean value = mapper.readValue(src, MyBean.class); // 'src' can be File, InputStream, Reader, String
//MyBean value = mapper.readValue(src, MyBean.class); // 'src' can be File, InputStream, Reader, String
puts ("mybean", value);
Object root = mapper.readValue(src, Object.class);
Map<String,Object> rootAsMap = mapper.readValue(src, Map.class);
puts ("root", root);
puts ("rootAsMap", rootAsMap);
MyBean myBean1 = new MyBean(); myBean1.name = "Diana";
MyBean myBean2 = new MyBean(); myBean2.name = "Rick";
dst = File.createTempFile("empList", ".json");
final List<MyBean> list = Lists.list( myBean1, myBean2 );
puts ("json string", mapper.writeValueAsString( list ));
mapper.writeValue( dst, list );
src = dst;
List<MyBean> beans = mapper.readValue(src, List.class, MyBean.class);
puts ("mybeans", beans);
}
}
Output from the above listing:
json string {"name":"Rick"}
mybean MyBean{name='Rick'}
root {name=Rick}
rootAsMap {name=Rick}
json string [{"name":"Diana"},{"name":"Rick"}]
mybeans [MyBean{name='Diana'}, MyBean{name='Rick'}]
Full Data Binding (POJO) Example
Boon's ObjectMapper "just works" for mapping JSON data into plain old Java objects ("POJOs"). For example, given JSON data:
{
"gender": "MALE",
"name": {
"first": "Richard",
"last": "Hightower"
},
"verified": true
}
To create the above JSON file, you could serialize this Java POJO:
public class User {
public enum Gender { MALE, FEMALE };
public static class Name {
private String first, last;
public Name( String _first, String _last ) {
this.first = _first;
this.last = _last;
}
public String getFirst() { return first; }
public String getLast() { return last; }
public void setFirst(String s) { first = s; }
public void setLast(String s) { last = s; }
}
private Gender gender;
private Name name;
private boolean isVerified;
public Name getName() { return name; }
public boolean isVerified() { return isVerified; }
public Gender getGender() { return gender; }
public void setName(Name n) { name = n; }
public void setVerified(boolean b) { isVerified = b; }
public void setGender(Gender g) { gender = g; }
}
To produce the above JSON, you can create a serialize User as follows:
ObjectMapper mapper = JsonFactory.create();
User user = new User();
user.setGender( User.Gender.MALE );
user.setName(new User.Name("Richard", "Hightower"));
user.setVerified( true );
puts ( mapper.writeValueAsString( user ) );
This JSON gets produced:
{
"gender": "MALE",
"name": {
"first": "Richard",
"last": "Hightower"
},
"verified": true
}
It is just as easy to read and write files:
Now to write/serialize and then read/deserialize this user to/fro to a file.
Write to a file:
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
Read from a file
User userFromFile = mapper.readValue( file, User.class );
puts ( userFromFile );
The puts method is like System.out.println of sorts and is part of Boon.
Boon also works with Streams, Readers, Writers, etc.
Read from a inputStream using JDK 1.7 Files utility:
Path path = Paths.get(file.toString());
InputStream inputStream = Files.newInputStream(path);
User userFromInput = mapper.readValue( inputStream, User.class );
puts ( "userFromInput", userFromInput );
Read from a reader using JDK 1.7 Files utility:
Reader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 );
User userFromReader = mapper.readValue( reader, User.class );
puts ( "userFromReader", userFromReader );
Boon ships with an IO library that makes it easy to read/write from URLs, and other file systems:
User userFromURL = mapper.readValue( IO.read("http://fromsomewebsite/user.json"), User.class );
puts ( "userFromURL", userFromURL );
Boon also makes making JSON calls to REST webservices easy as you can use Boon's HTTP utilities as follows:
String results = HTTP.postJSON("http://foo.com/bar/add/user", mapper.writeValueAsString( user ) );
AddUserResponse response = mapper.readValue( results, AddUserResponse.class );
Here is an example of getting a listing of users from a REST call:
List <User> userList = mapper.readValue( HTTP.getJSON("http://foo.com/bar/user/listing"),
List.class, User.class );
package org.boon.json;
import org.boon.Lists;
import org.boon.core.Dates;
import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.boon.Boon.puts;
/**
* Created by rick on 1/4/14.
*/
public class JsonTutorial {
public static class MyBean {
String name = "Rick";
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
'}';
}
}
public static class User {
public enum Gender { MALE, FEMALE };
public static class Name {
private String first, last;
public Name( String _first, String _last ) {
this.first = _first;
this.last = _last;
}
public String getFirst() { return first; }
public String getLast() { return last; }
public void setFirst(String s) { first = s; }
public void setLast(String s) { last = s; }
@Override
public String toString() {
return "Name{" +
"first='" + first + '\'' +
", last='" + last + '\'' +
'}';
}
}
private Gender gender;
private Name name;
private boolean verified;
private Date birthDate;
public Name getName() { return name; }
public boolean isVerified() { return verified; }
public Gender getGender() { return gender; }
public void setName(Name n) { name = n; }
public void setVerified(boolean b) { verified = b; }
public void setGender(Gender g) { gender = g; }
public Date getBirthDate() { return birthDate; }
public void setBirthDate( Date birthDate ) { this.birthDate = birthDate; }
@Override
public String toString() {
return "User{" +
"gender=" + gender +
", name=" + name +
", isVerified=" + verified +
'}';
}
}
public static void part1 () throws Exception {
MyBean myBean = new MyBean();
File dst = File.createTempFile("emp", ".json");
ObjectMapper mapper = JsonFactory.create();
puts ("json string", mapper.writeValueAsString( myBean ));
mapper.writeValue( dst, myBean ); // where 'dst' can be File, OutputStream or Writer
File src = dst;
MyBean value = mapper.readValue(src, MyBean.class); // 'src' can be File, InputStream, Reader, String
puts ("mybean", value);
Object root = mapper.readValue(src, Object.class);
Map<String,Object> rootAsMap = mapper.readValue(src, Map.class);
puts ("root", root);
puts ("rootAsMap", rootAsMap);
MyBean myBean1 = new MyBean(); myBean1.name = "Diana";
MyBean myBean2 = new MyBean(); myBean2.name = "Rick";
dst = File.createTempFile("empList", ".json");
final List<MyBean> list = Lists.list( myBean1, myBean2 );
puts ("json string", mapper.writeValueAsString( list ));
mapper.writeValue( dst, list );
src = dst;
List<MyBean> beans = mapper.readValue(src, List.class, MyBean.class);
puts ("mybeans", beans);
}
public static void part2 () throws Exception {
ObjectMapper mapper = JsonFactory.create();
User user = new User();
user.setGender( User.Gender.MALE );
user.setName(new User.Name("Richard", "Hightower"));
user.setVerified( true );
user.setBirthDate( Dates.getUSDate( 5, 25, 1980 ) );
puts (mapper.writeValueAsString( user ));
//Now to write and then read this as a file.
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
User userFromFile = mapper.readValue( file, User.class );
puts ( "userFromFile", userFromFile );
Path path = Paths.get(file.toString());
InputStream inputStream = Files.newInputStream(path);
User userFromInput = mapper.readValue( inputStream, User.class );
puts ( "userFromInput", userFromInput );
Reader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 );
User userFromReader = mapper.readValue( reader, User.class );
puts ( "userFromReader", userFromReader );
}
public static void main (String... args) throws Exception {
part1();
part2();
}
}
Going back to our user example. By default if you print out a date, it looks like this:
ObjectMapper mapper = JsonFactory.create();
puts ( mapper.writeValueAsString( user ) );
The above generates this:
{
"gender": "MALE",
"name": {
"first": "Richard",
"last": "Hightower"
},
"verified": true,
"birthDate": 328147200766
}
...
//user was initialized as follows:
static User user = new User();
static {
user.setGender( User.Gender.MALE );
user.setName(new User.Name("Richard", "Hightower"));
user.setVerified( true );
user.setBirthDate( Dates.getUSDate( 5, 25, 1980 ) );
}
The issue is 328147200766 is not very friendly way to write a date. One of the nice things about JSON is it is readable, and that date is not very readable. JavaScript formats a date using a standard format, and if you are working with JavaScript, it can easily parse this standard format if you enable it. (Java Microservices Consulting)
Since this date format is the de facto standard, Boon makes it easy to use this format as follows:
ObjectMapper mapper = JsonFactory.create();
puts ( mapper.writeValueAsString( user ) );
Generates this user with a human readable date.
{
"gender": "MALE",
"name": {
"first": "Richard",
"last": "Hightower"
},
"verified": true,
"birthDate": "1980-05-26T00:00:00.014Z"
}
The date gets converted into GMT and then converted into a human readable string.
You can customize how Strings, Dates, Arrays, Collections, etc. are output quite easily, but that is beyond the scope of the five minute guide.
Boon can read and write this date format on the fly, and this is the date format that JavaScript browser use to convey dates.
Example of reading LONG date format on the fly to/for:
ObjectMapper mapper = JsonFactory.create();
puts ( mapper.writeValueAsString( user ) );
User user2 = mapper.readValue( mapper.writeValueAsString( user ), User.class );
puts (user2);
Example of reading JSON string date format on the fly to/for:
ObjectMapper mapper = JsonFactory.createUseJSONDates();
puts ( mapper.writeValueAsString( user ) );
User user2 = mapper.readValue( mapper.writeValueAsString( user ), User.class );
puts (user2);
Output:
{"gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":328147200193}
User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
{"gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.193Z"}
User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
Notice that the long and the JSON style date are written and read with ease.
In cases where you do not have (and don't want to create) specific Java classes to bind JSON to/from, "Untyped data binding" may be a better approach. It is used same way as full data binding, except that the formal binding type is specified simply as Object.class (or Map.class, List.class, String[].class, int[].class, etc. if more specific typing is wanted). The earlier binding of JSON that represent User data could have been done by:
Map<String,Object> userData = mapper.readValue(new File("user.json"), Map.class); and userData would be like one we would explicit construct by:
ObjectMapper mapper = JsonFactory.createUseJSONDates();
puts( mapper.writeValueAsString( user ) );
//Now to write and then read this as a file.
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
//Reading the value as Object returns a list of map, and in this case a map.
Object userFromFile = mapper.readValue( file, Object.class );
puts( "userFromFile", "type", userFromFile.getClass(), "value", userFromFile );
//Here we can read it as a map.
Map<String, Object> map = (Map<String, Object>) mapper.readValue( file, Map.class );
puts( "userFromFile", "type", map.getClass(), "value", map );
//We can access this individual properties with the map interface
puts( "userFromFile.name", "type", map.get("name").getClass(),
"value", map.get("name") );
//We can even get to the birthdate, notice birthdate gets converted to a date because
//boon recognizes the date format string
puts( "userFromFile.birthDate", "type", map.get("birthDate").getClass(),
"value", map.get("birthDate") );
//Gender gets read in as a string since we did not have an class to let us know it was an enum
puts( "userFromFile.gender", "type", map.get("gender").getClass(),
"value", map.get("gender") );
//Since this is boon, we can always convert the map into a field later.
User userFromMap =
MapObjectConversion.fromMap(
map, User.class);
puts ( userFromMap );
Output
{"gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.826Z"}
userFromFile type class org.boon.core.value.LazyValueMap value {gender=MALE, birthDate=Sun May 25 17:00:00 PDT 1980, verified=true, name={last=Hightower, first=Richard}}
userFromFile type class org.boon.core.value.LazyValueMap value {gender=MALE, birthDate=Sun May 25 17:00:00 PDT 1980, verified=true, name={last=Hightower, first=Richard}}
userFromFile.name type class org.boon.core.value.LazyValueMap value {last=Hightower, first=Richard}
userFromFile.birthDate type class java.util.Date value Sun May 25 17:00:00 PDT 1980
userFromFile.gender type class java.lang.String value MALE
User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
The class LazyValueMap implements java.util.Map. The boon utility class MapObjectConversion can read to/fro maps just like ObjectMapper can read to/fro JSON.
This obviously works both ways: if you did construct such a Map (or bind from JSON and modify), you could write out just as before, by:
mapper.writeValue(new File("user-modified.json"), userMap);
How does this work? By specifying Map.class, we do not specify generic key/value types. But ObjectMapper does know how to bind JSON data to and from Maps (and Lists, arrays, wrapper types, primitives, primitive arrays), and does just that. Fundamentally JSON data has no "real" type as far as Boon is concerned -- if it can be properly mapped to a type you give, it will be mapped.
In addition to binding to POJOs and "simple" types, there is one additional variant: that of binding to generic (typed) containers. This case requires special handling due to so-called Type Erasure (used by Java to implement generics in somewhat backwards compatible way), which prevents you from using something like Collection.class (which does not compile).
So if you want to bind data into a List you will need to use:
Working with generics
mapper.writeValue( file, users );
List<User> userList = mapper.readValue( file, List.class, User.class );
puts (userList);
Output
[
{
"gender": "FEMALE",
"name": {
"first": "Diana",
"last": "Hightower"
},
"verified": true,
"birthDate": "1984-08-22T00:00:00.002Z"
},
{
"gender": "MALE",
"name": {
"first": "Rick",
"last": "Hightower"
},
"verified": true,
"birthDate": "1980-05-26T00:00:00.941Z"
}
]
Full listing so far:
package org.boon.json;
import org.boon.Lists;
import org.boon.core.Dates;
import org.boon.core.reflection.BeanUtils;
import org.boon.core.reflection.MapObjectConversion;
import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.boon.Boon.puts;
/**
* Created by rick on 1/4/14.
*/
public class JsonTutorial {
public static class MyBean {
String name = "Rick";
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
'}';
}
}
public static class User {
public enum Gender {MALE, FEMALE}
;
public static class Name {
private String first, last;
public Name( String _first, String _last ) {
this.first = _first;
this.last = _last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
public void setFirst( String s ) {
first = s;
}
public void setLast( String s ) {
last = s;
}
@Override
public String toString() {
return "Name{" +
"first='" + first + '\'' +
", last='" + last + '\'' +
'}';
}
}
private Gender gender;
private Name name;
private boolean verified;
private Date birthDate;
public Name getName() {
return name;
}
public boolean isVerified() {
return verified;
}
public Gender getGender() {
return gender;
}
public void setName( Name n ) {
name = n;
}
public void setVerified( boolean b ) {
verified = b;
}
public void setGender( Gender g ) {
gender = g;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate( Date birthDate ) {
this.birthDate = birthDate;
}
@Override
public String toString() {
return "User{" +
"gender=" + gender +
", name=" + name +
", verified=" + verified +
", birthDate=" + birthDate +
'}';
}
}
static User user = new User();
static {
user.setGender( User.Gender.MALE );
user.setName( new User.Name( "Richard", "Hightower" ) );
user.setVerified( true );
user.setBirthDate( Dates.getUSDate( 5, 25, 1980 ) );
}
public static void part1() throws Exception {
MyBean myBean = new MyBean();
File dst = File.createTempFile( "emp", ".json" );
ObjectMapper mapper = JsonFactory.create();
puts( "json string", mapper.writeValueAsString( myBean ) );
mapper.writeValue( dst, myBean ); // where 'dst' can be File, OutputStream or Writer
File src = dst;
MyBean value = mapper.readValue( src, MyBean.class ); // 'src' can be File, InputStream, Reader, String
puts( "mybean", value );
Object root = mapper.readValue( src, Object.class );
Map<String, Object> rootAsMap = mapper.readValue( src, Map.class );
puts( "root", root );
puts( "rootAsMap", rootAsMap );
MyBean myBean1 = new MyBean();
myBean1.name = "Diana";
MyBean myBean2 = new MyBean();
myBean2.name = "Rick";
dst = File.createTempFile( "empList", ".json" );
final List<MyBean> list = Lists.list( myBean1, myBean2 );
puts( "json string", mapper.writeValueAsString( list ) );
mapper.writeValue( dst, list );
src = dst;
List<MyBean> beans = mapper.readValue( src, List.class, MyBean.class );
puts( "mybeans", beans );
}
public static void part2() throws Exception {
ObjectMapper mapper = JsonFactory.create();
puts( mapper.writeValueAsString( user ) );
//Now to write and then read this as a file.
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
User userFromFile = mapper.readValue( file, User.class );
puts( "userFromFile", userFromFile );
Path path = Paths.get( file.toString() );
InputStream inputStream = Files.newInputStream( path );
User userFromInput = mapper.readValue( inputStream, User.class );
puts( "userFromInput", userFromInput );
Reader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 );
User userFromReader = mapper.readValue( reader, User.class );
puts( "userFromReader", userFromReader );
}
public static void part3() throws Exception {
part3_1();
part3_2();
}
public static void part3_1() throws Exception {
ObjectMapper mapper = JsonFactory.create();
puts( mapper.writeValueAsString( user ) );
User user2 = mapper.readValue( mapper.writeValueAsString( user ), User.class );
puts( user2 );
}
public static void part3_2() throws Exception {
ObjectMapper mapper = JsonFactory.createUseJSONDates();
puts( mapper.writeValueAsString( user ) );
User user2 = mapper.readValue( mapper.writeValueAsString( user ), User.class );
puts( user2 );
}
public static void part5() throws Exception {
puts ("\n\n\n", "\npart5");
ObjectMapper mapper = JsonFactory.createUseJSONDates();
final User diana = BeanUtils.copy( user );
final User rick = BeanUtils.copy( user );
diana.getName().setFirst( "Diana" );
rick.getName().setFirst( "Rick" );
diana.setBirthDate( Dates.getUSDate( 8, 21, 1984 ) );
File file = File.createTempFile( "userList", ".json" );
List<User> users = Lists.list( diana, rick );
mapper.writeValue( file, users );
List<User> userList = mapper.readValue( file, List.class, User.class );
puts (userList);
puts ( mapper.writeValueAsString( userList ) );
}
public static void part4() throws Exception {
ObjectMapper mapper = JsonFactory.createUseJSONDates();
puts( mapper.writeValueAsString( user ) );
//Now to write and then read this as a file.
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
Object userFromFile = mapper.readValue( file, Object.class );
puts( "userFromFile", "type", userFromFile.getClass(), "value", userFromFile );
Map<String, Object> map = (Map<String, Object>) mapper.readValue( file, Map.class );
puts( "userFromFile", "type", map.getClass(), "value", map );
puts( "userFromFile.name", "type", map.get("name").getClass(),
"value", map.get("name") );
puts( "userFromFile.birthDate", "type", map.get("birthDate").getClass(),
"value", map.get("birthDate") );
puts( "userFromFile.gender", "type", map.get("gender").getClass(),
"value", map.get("gender") );
User userFromMap =
MapObjectConversion.fromMap(
map, User.class);
puts ( userFromMap );
}
public static void part6() throws Exception {
puts ("\n\n\n", "\npart5");
ObjectMapper mapper = JsonFactory.createUseJSONDates();
final User diana = BeanUtils.copy( user );
final User rick = BeanUtils.copy( user );
diana.getName().setFirst( "Diana" );
diana.setGender( User.Gender.FEMALE );
rick.getName().setFirst( "Rick" );
diana.setBirthDate( Dates.getUSDate( 8, 21, 1984 ) );
File file = File.createTempFile( "userList", ".json" );
List<User> users = Lists.list( diana, rick );
mapper.writeValue( file, users );
List<User> userList = mapper.readValue( file, List.class, User.class );
puts (userList);
puts ( mapper.writeValueAsString( userList ) );
}
public static void main( String... args ) throws Exception {
part1();
part2();
part3();
part4();
part5();
}
}
Working with annotations. Boon in general and Boon JSON support specifically is not driven entirely by annotations. There is both a JsonParserFactory and a JsonSerializerFactory and with those you can override certain behaviors of JSON parsing and serializing.
There are some annotations that Boon JSON does recognize and they are JsonIgnore, JsonInclude and JsonIncludeProperties. These JSON properties exist in Boon, but Boon will also recognize these annotations from JsonSmart and Jackson and handle them the same way.
By default (can be overridden with the factory) Boon will not write out nulls, empty lists or values that are default values (int primitive has a default of 0, boolean primitive has a default of false). If you want a value to be written out even if it is empty, null, false or 0, you can use the @JsonInclude annotation.
Boon by default outputs all properties and/or fields of an object. Let's say you had an employee directory and an employee has a SSN. You don't want to output an SSN so you can leave it out with a @JsonIgnore. Let's show how these work in practice.
Continuing our example before, let's create an SSN that we don't want output with JsonIgnore and a status string that we want written even if it is null or empty. (Java Microservices Consulting)
Using @JsonIgnore and @JsonInclude
public class User {
@JsonIgnore
private String ssn = "555-55-5555";
@JsonInclude
private String status = null;
Write them out
/* Write users out to file. */
mapper.writeValue( file, users );
/* Reader Users back from file. */
List<User> userList = mapper.readValue( file, List.class, User.class );
puts ("userListBeansReadFromFile", userList);
/* Inspect the JSON of the users from the file. */
puts ("usersFromFileAsJSON", mapper.writeValueAsString( userList ) );
Output
usersFromFileAsJSON
[
{
"status": null,
"gender": "FEMALE",
"name": {
"first": "Diana",
"last": "Hightower"
},
"verified": true,
"birthDate": "1984-08-22T00:00:00.910Z"
},
{
"status": null,
"gender": "MALE",
"name": {
"first": "Rick",
"last": "Hightower"
},
"verified": true,
"birthDate": "1980-05-26T00:00:00.822Z"
}
]
package org.boon.json;
import org.boon.Lists;
import org.boon.core.Dates;
import org.boon.core.reflection.BeanUtils;
import org.boon.core.reflection.MapObjectConversion;
import org.boon.json.annotations.JsonIgnore;
import org.boon.json.annotations.JsonInclude;
import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.boon.Boon.puts;
/**
* Created by rick on 1/4/14.
*/
public class JsonTutorial {
public static class MyBean {
String name = "Rick";
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
'}';
}
}
public static class User {
@JsonIgnore
private String ssn = "555-55-5555";
@JsonInclude
private String status = null;
public enum Gender {MALE, FEMALE}
public static class Name {
private String first, last;
public Name( String _first, String _last ) {
this.first = _first;
this.last = _last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
public void setFirst( String s ) {
first = s;
}
public void setLast( String s ) {
last = s;
}
@Override
public String toString() {
return "Name{" +
"first='" + first + '\'' +
", last='" + last + '\'' +
'}';
}
}
private Gender gender;
private Name name;
private boolean verified;
private Date birthDate;
public Name getName() {
return name;
}
public boolean isVerified() {
return verified;
}
public Gender getGender() {
return gender;
}
public void setName( Name n ) {
name = n;
}
public void setVerified( boolean b ) {
verified = b;
}
public void setGender( Gender g ) {
gender = g;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate( Date birthDate ) {
this.birthDate = birthDate;
}
@Override
public String toString() {
return "User{" +
"gender=" + gender +
", name=" + name +
", verified=" + verified +
", birthDate=" + birthDate +
'}';
}
}
static User user = new User();
static {
user.setGender( User.Gender.MALE );
user.setName( new User.Name( "Richard", "Hightower" ) );
user.setVerified( true );
user.setBirthDate( Dates.getUSDate( 5, 25, 1980 ) );
}
public static void part1() throws Exception {
MyBean myBean = new MyBean();
File dst = File.createTempFile( "emp", ".json" );
ObjectMapper mapper = JsonFactory.create();
puts( "json string", mapper.writeValueAsString( myBean ) );
mapper.writeValue( dst, myBean ); // where 'dst' can be File, OutputStream or Writer
File src = dst;
MyBean value = mapper.readValue( src, MyBean.class ); // 'src' can be File, InputStream, Reader, String
puts( "mybean", value );
Object root = mapper.readValue( src, Object.class );
Map<String, Object> rootAsMap = mapper.readValue( src, Map.class );
puts( "root", root );
puts( "rootAsMap", rootAsMap );
MyBean myBean1 = new MyBean();
myBean1.name = "Diana";
MyBean myBean2 = new MyBean();
myBean2.name = "Rick";
dst = File.createTempFile( "empList", ".json" );
final List<MyBean> list = Lists.list( myBean1, myBean2 );
puts( "json string", mapper.writeValueAsString( list ) );
mapper.writeValue( dst, list );
src = dst;
List<MyBean> beans = mapper.readValue( src, List.class, MyBean.class );
puts( "mybeans", beans );
}
public static void part2() throws Exception {
ObjectMapper mapper = JsonFactory.create();
puts( mapper.writeValueAsString( user ) );
//Now to write and then read this as a file.
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
User userFromFile = mapper.readValue( file, User.class );
puts( "userFromFile", userFromFile );
Path path = Paths.get( file.toString() );
InputStream inputStream = Files.newInputStream( path );
User userFromInput = mapper.readValue( inputStream, User.class );
puts( "userFromInput", userFromInput );
Reader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 );
User userFromReader = mapper.readValue( reader, User.class );
puts( "userFromReader", userFromReader );
}
public static void part3() throws Exception {
part3_1();
part3_2();
}
public static void part3_1() throws Exception {
ObjectMapper mapper = JsonFactory.create();
puts( mapper.writeValueAsString( user ) );
User user2 = mapper.readValue( mapper.writeValueAsString( user ), User.class );
puts( user2 );
}
public static void part3_2() throws Exception {
ObjectMapper mapper = JsonFactory.createUseJSONDates();
puts( mapper.writeValueAsString( user ) );
User user2 = mapper.readValue( mapper.writeValueAsString( user ), User.class );
puts( user2 );
}
public static void part5() throws Exception {
puts ("\n\n\n", "\npart5");
ObjectMapper mapper = JsonFactory.createUseJSONDates();
final User diana = BeanUtils.copy( user );
final User rick = BeanUtils.copy( user );
diana.getName().setFirst( "Diana" );
rick.getName().setFirst( "Rick" );
diana.setBirthDate( Dates.getUSDate( 8, 21, 1984 ) );
File file = File.createTempFile( "userList", ".json" );
List<User> users = Lists.list( diana, rick );
mapper.writeValue( file, users );
List<User> userList = mapper.readValue( file, List.class, User.class );
puts (userList);
puts ( mapper.writeValueAsString( userList ) );
}
public static void part4() throws Exception {
ObjectMapper mapper = JsonFactory.createUseJSONDates();
puts( mapper.writeValueAsString( user ) );
//Now to write and then read this as a file.
File file = File.createTempFile( "user", ".json" );
mapper.writeValue( file, user );
Object userFromFile = mapper.readValue( file, Object.class );
puts( "userFromFile", "type", userFromFile.getClass(), "value", userFromFile );
Map<String, Object> map = (Map<String, Object>) mapper.readValue( file, Map.class );
puts( "userFromFile", "type", map.getClass(), "value", map );
puts( "userFromFile.name", "type", map.get("name").getClass(),
"value", map.get("name") );
puts( "userFromFile.birthDate", "type", map.get("birthDate").getClass(),
"value", map.get("birthDate") );
puts( "userFromFile.gender", "type", map.get("gender").getClass(),
"value", map.get("gender") );
User userFromMap =
MapObjectConversion.fromMap(
map, User.class);
puts ( userFromMap );
}
public static void part6() throws Exception {
puts ("\n\n\n", "\npart6");
ObjectMapper mapper = JsonFactory.createUseJSONDates();
final User diana = BeanUtils.copy( user );
final User rick = BeanUtils.copy( user );
diana.getName().setFirst( "Diana" );
diana.setGender( User.Gender.FEMALE );
rick.getName().setFirst( "Rick" );
diana.setBirthDate( Dates.getUSDate( 8, 21, 1984 ) );
File file = File.createTempFile( "userList", ".json" );
List<User> users = Lists.list( diana, rick );
mapper.writeValue( file, users );
List<User> userList = mapper.readValue( file, List.class, User.class );
puts (userList);
puts ( mapper.writeValueAsString( userList ) );
}
public static void part7() throws Exception {
puts ("\n\n\n", "\npart7");
ObjectMapper mapper = JsonFactory.createUseAnnotations( true );
/* Create two users. */
final User diana = BeanUtils.copy( user );
final User rick = BeanUtils.copy( user );
diana.getName().setFirst( "Diana" );
diana.setGender( User.Gender.FEMALE );
rick.getName().setFirst( "Rick" );
diana.setBirthDate( Dates.getUSDate( 8, 21, 1984 ) );
File file = File.createTempFile( "userList", ".json" );
List<User> users = Lists.list( diana, rick );
/* Inspect the JSON of the users from the file. */
puts ("users", mapper.writeValueAsString( users ) );
/* Write users out to file. */
mapper.writeValue( file, users );
/* Reader Users back from file. */
List<User> userList = mapper.readValue( file, List.class, User.class );
puts ("userListBeansReadFromFile", userList);
/* Inspect the JSON of the users from the file. */
puts ("usersFromFileAsJSON", mapper.writeValueAsString( userList ) );
}
public static void main( String... args ) throws Exception {
part1();
part2();
part3();
part4();
part5();
part6();
part7();
}
}
Output so far
json string {"name":"Rick"}
mybean MyBean{name='Rick'}
root {name=Rick}
rootAsMap {name=Rick}
json string [{"name":"Diana"},{"name":"Rick"}]
mybeans [MyBean{name='Diana'}, MyBean{name='Rick'}]
{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":328147200833}
userFromFile User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
userFromInput User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
userFromReader User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":328147200833}
User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.833Z"}
User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Richard","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.833Z"}
userFromFile type class org.boon.core.value.LazyValueMap value {ssn=555-55-5555, gender=MALE, birthDate=Sun May 25 17:00:00 PDT 1980, verified=true, name={last=Hightower, first=Richard}}
userFromFile type class org.boon.core.value.LazyValueMap value {ssn=555-55-5555, gender=MALE, birthDate=Sun May 25 17:00:00 PDT 1980, verified=true, name={last=Hightower, first=Richard}}
userFromFile.name type class org.boon.core.value.LazyValueMap value {last=Hightower, first=Richard}
userFromFile.birthDate type class java.util.Date value Sun May 25 17:00:00 PDT 1980
userFromFile.gender type class java.lang.String value MALE
User{gender=MALE, name=Name{first='Richard', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}
part5
[User{gender=MALE, name=Name{first='Diana', last='Hightower'}, verified=true, birthDate=Tue Aug 21 17:00:00 PDT 1984}, User{gender=MALE, name=Name{first='Rick', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}]
[{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Diana","last":"Hightower"},"verified":true,"birthDate":"1984-08-22T00:00:00.989Z"},{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Rick","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.833Z"}]
part6
[User{gender=FEMALE, name=Name{first='Diana', last='Hightower'}, verified=true, birthDate=Tue Aug 21 17:00:00 PDT 1984}, User{gender=MALE, name=Name{first='Rick', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}]
[{"ssn":"555-55-5555","gender":"FEMALE","name":{"first":"Diana","last":"Hightower"},"verified":true,"birthDate":"1984-08-22T00:00:00.991Z"},{"ssn":"555-55-5555","gender":"MALE","name":{"first":"Rick","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.833Z"}]
part7
users [{"status":null,"gender":"FEMALE","name":{"first":"Diana","last":"Hightower"},"verified":true,"birthDate":"1984-08-22T00:00:00.992Z"},{"status":null,"gender":"MALE","name":{"first":"Rick","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.833Z"}]
userListBeansReadFromFile [User{gender=FEMALE, name=Name{first='Diana', last='Hightower'}, verified=true, birthDate=Tue Aug 21 17:00:00 PDT 1984}, User{gender=MALE, name=Name{first='Rick', last='Hightower'}, verified=true, birthDate=Sun May 25 17:00:00 PDT 1980}]
usersFromFileAsJSON [{"status":null,"gender":"FEMALE","name":{"first":"Diana","last":"Hightower"},"verified":true,"birthDate":"1984-08-22T00:00:00.992Z"},{"status":null,"gender":"MALE","name":{"first":"Rick","last":"Hightower"},"verified":true,"birthDate":"1980-05-26T00:00:00.833Z"}]
Process finished with exit code 0
At times it is nice to have a light weight parser that can handling Java primitive types, Strings, etc.
Boon JSON can read int, float, double, long, int arrays, dates, strings, etc.
ObjectMapper mapper = JsonFactory.create();
String intStr = "123456";
int someNumber = mapper.parser().parseInt( intStr );
boolean ok = someNumber == 123456 || die( "" + someNumber );
String jsonArray = "[0,1,2,3,4,5,6,7,8]";
int [] intArray = mapper.parser().parseIntArray( jsonArray );
ok |= Arrays.equals( new int[]{1,2,3,4,5,6,7,8}, intArray );
String jsonMap = "{\"race\":true, \"speedup\": false, \"name\": \"bob\"}";
Map <String, Object> map = mapper.parser().parseMap( jsonMap );
ok |= ( map.get("race") == true && map.get("name").equals( "bob" ) ) || die(map.toString());
puts("ok?", ok);
The main interface to Boon parsing is not ObjectMapper, but JsonParser. The ObjectMapper interface is the one most people are familiar with thanks to the success of Jackson.
You could easily use JsonParser to read configuration settings stored as strings in a database or a properties files, etc. It allows you to parse arrays, strings, maps easily. No fuss no muss. No need for object serialization or annotations. Boon's JsonParser is first and foremost a fast and lightweight parser.
Notice as you ponder the overview of JsonParser that it supports BigDecmimal and BigInteger as well.
package org.boon.json;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
import java.util.Map;
public interface JsonParser {
Map<String, Object> parseMap( String value );
Map<String, Object> parseMap( char [] value );
Map<String, Object> parseMap( byte[] value );
Map<String, Object> parseMap( byte[] value, Charset charset );
Map<String, Object> parseMap( InputStream value, Charset charset );
Map<String, Object> parseMap( CharSequence value );
Map<String, Object> parseMap( InputStream value );
Map<String, Object> parseMap( Reader value );
Map<String, Object> parseMapFromFile( String file );
<T> List<T> parseList( Class<T> componentType, String jsonString );
<T> List<T> parseList( Class<T> componentType, InputStream input );
<T> List<T> parseList( Class<T> componentType, Reader reader );
<T> List<T> parseList( Class<T> componentType, InputStream input, Charset charset );
<T> List<T> parseList( Class<T> componentType, byte[] jsonBytes );
<T> List<T> parseList( Class<T> componentType, byte[] jsonBytes, Charset charset );
<T> List<T> parseList( Class<T> componentType, char[] chars );
<T> List<T> parseList( Class<T> componentType, CharSequence jsonSeq );
<T> List<T> parseListFromFile( Class<T> componentType, String fileName );
<T> T parse( Class<T> type, String jsonString );
<T> T parse( Class<T> type, byte[] bytes );
<T> T parse( Class<T> type, byte[] bytes, Charset charset );
<T> T parse( Class<T> type, CharSequence charSequence );
<T> T parse( Class<T> type, char[] chars );
<T> T parse( Class<T> type, Reader reader );
<T> T parse( Class<T> type, InputStream input );
<T> T parse( Class<T> type, InputStream input, Charset charset );
<T> T parseDirect( Class<T> type, byte[] value );
<T> T parseAsStream( Class<T> type, byte[] value );
<T> T parseFile( Class<T> type, String fileName);
int parseInt( String jsonString );
int parseInt( InputStream input );
int parseInt( InputStream input, Charset charset );
int parseInt( byte[] jsonBytes );
int parseInt( byte[] jsonBytes, Charset charset );
int parseInt( char[] chars );
int parseInt( CharSequence jsonSeq );
int parseIntFromFile( String fileName );
long parseLong( String jsonString );
long parseLong( InputStream input );
long parseLong( InputStream input, Charset charset );
long parseLong( byte[] jsonBytes );
long parseLong( byte[] jsonBytes, Charset charset );
long parseLong( char[] chars );
long parseLong( CharSequence jsonSeq );
long parseLongFromFile( String fileName );
double parseDouble( String value );
double parseDouble( InputStream value );
double parseDouble( byte[] value );
double parseDouble( char[] value );
double parseDouble( CharSequence value );
double parseDouble( byte[] value, Charset charset );
double parseDouble( InputStream value, Charset charset );
double parseDoubleFromFile( String fileName );
float parseFloat( String value );
float parseFloat( InputStream value );
float parseFloat( byte[] value );
float parseFloat( char[] value );
float parseFloat( CharSequence value );
float parseFloat( byte[] value, Charset charset );
float parseFloat( InputStream value, Charset charset );
float parseFloatFromFile( String fileName );
BigDecimal parseBigDecimal( String value );
BigDecimal parseBigDecimal( InputStream value );
BigDecimal parseBigDecimal( byte[] value );
BigDecimal parseBigDecimal( char[] value );
BigDecimal parseBigDecimal( CharSequence value );
BigDecimal parseBigDecimal( byte[] value, Charset charset );
BigDecimal parseBigDecimal( InputStream value, Charset charset );
BigDecimal parseBigDecimalFromFile( String fileName );
BigInteger parseBigInteger( String value );
BigInteger parseBigInteger( InputStream value );
BigInteger parseBigInteger( byte[] value );
BigInteger parseBigInteger( char[] value );
BigInteger parseBigInteger( CharSequence value );
BigInteger parseBigInteger( byte[] value, Charset charset );
BigInteger parseBigInteger( InputStream value, Charset charset );
BigInteger parseBigIntegerFile( String fileName );
Date parseDate( String jsonString );
Date parseDate( InputStream input );
Date parseDate( InputStream input, Charset charset );
Date parseDate( byte[] jsonBytes );
Date parseDate( byte[] jsonBytes, Charset charset );
Date parseDate( char[] chars );
Date parseDate( CharSequence jsonSeq );
Date parseDateFromFile( String fileName );
short parseShort ( String jsonString );
byte parseByte ( String jsonString );
char parseChar ( String jsonString );
<T extends Enum> T parseEnum ( Class<T> type, String jsonString );
public char [] parseCharArray ( String jsonString );
public byte [] parseByteArray ( String jsonString );
public short [] parseShortArray ( String jsonString );
public int [] parseIntArray ( String jsonString );
public float [] parseFloatArray ( String jsonString );
public double [] parseDoubleArray ( String jsonString );
public long [] parseLongArray ( String jsonString );
Object parse( String jsonString );
Object parse( byte[] bytes );
Object parse( byte[] bytes, Charset charset );
Object parse( CharSequence charSequence );
Object parse( char[] chars );
Object parse( Reader reader );
Object parse( InputStream input );
Object parse( InputStream input, Charset charset );
Object parseDirect( byte[] value );
Object parseAsStream( byte[] value );
Object parseFile( String fileName);
void close();
}
With Boon you can customize the output and input with JsonParserFactory and JsonSerializerFactory as follows:
You can basically install custom field serializers and type serializer on the factory:
AllTypes foo = new AllTypes ();
foo.ingnoreMe = "THIS WILL NOT PASS";
foo.ignoreMe2 = "THIS WILL NOT PASS EITHER";
foo.ignoreMe3 = "THIS WILL NOT PASS TOO";
foo.setDate ( new Date() );
foo.setBar ( FooEnum.BAR );
foo.setFoo ( FooEnum.FOO );
foo.setString ( "Hi Mom" );
AllTypes foo2 = BeanUtils.copy( foo );
foo.setAllType ( foo2 );
foo2.setString ( "Hi Dad" );
foo.setAllTypes ( Lists.list( BeanUtils.copy( foo2 ), BeanUtils.copy( foo2 )) );
final JsonSerializer serializer = new JsonSerializerFactory ()
.useAnnotations ()
.addFilter ( new FieldFilter () {
@Override
public boolean include ( Object parent, FieldAccess fieldAccess ) {
if ( fieldAccess.getName().equals( "ignoreMe3" ) ) {
return false;
} else {
return true;
}
}
} ).addPropertySerializer ( new CustomFieldSerializer () {
@Override
public boolean serializeField ( JsonSerializerInternal serializer, Object parent,
FieldAccess fieldAccess, CharBuf builder ) {
if ( fieldAccess.getType ().equals ( long.class ) &&
fieldAccess.getName ().endsWith ( "Date" ) ) {
builder.addJsonFieldName ( fieldAccess.getName () );
Date date = Conversions.toDate ( fieldAccess.getLong ( parent ) );
final String jsonDateString = Dates.jsonDate ( date );
builder.add ( jsonDateString );
return true;
} else {
return false;
}
}
} ).addTypeSerializer ( FooBasket.class, new AbstractCustomObjectSerializer ( FooBasket.class ) {
@Override
public void serializeObject ( JsonSerializerInternal serializer, Object instance, CharBuf builder ) {
builder.addString ( "[\"wiki\",\"wiki\",\"wiki\"]" );
}
} )
.create ();
String json = serializer.serialize ( foo ).toString ();
puts (json);
boolean ok = json.contains ("[\"wiki\",\"wiki\",\"wiki\"]" ) || die();
puts (json);
AllTypes testMe = jsonParser.parse( AllTypes.class, json);
ok |= testMe.equals ( foo ) || die();
ok |= testMe.ingnoreMe == null || die();
puts (testMe.ignoreMe2);
ok |= testMe.ignoreMe2 == null || die();
puts (testMe.ignoreMe3);
ok |= testMe.ignoreMe3 == null || die();
ok |= testMe.someDate > 0 || die();
This is just a quick example. A full listing and explanation is due, but that would not fit in a five minute discussion. :)
The AllType class used in the example:
package org.boon.json;
import org.boon.json.annotations.JsonIgnore;
import org.boon.json.annotations.JsonIgnoreProperties;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@JsonIgnoreProperties ("ignoreMe2")
public class AllTypes {
public FooBasket getFooBasket () {
return fooBasket;
}
public void setFooBasket ( FooBasket fooBasket ) {
this.fooBasket = fooBasket;
}
FooBasket fooBasket = new FooBasket ();
String ignoreMe3;
String ignoreMe2;
int myInt;
boolean myBoolean;
short myShort;
long myLong;
String string;
String string2;
BigDecimal bigDecimal;
BigInteger bigInteger;
Date date;
float myFloat;
double myDouble;
byte myByte;
FooEnum foo;
FooEnum bar;
@JsonIgnore
String ingnoreMe;
long someDate = new Date ( ).getTime ();
AllTypes allType;
List<AllTypes> allTypes = new ArrayList<> ( );
public String getString2 () {
return string2;
}
public void setString2 ( String string2 ) {
this.string2 = string2;
}
public List<AllTypes> getAllTypes () {
return allTypes;
}
public void setAllTypes ( List<AllTypes> allTypes ) {
this.allTypes = allTypes;
}
public AllTypes getAllType () {
return allType;
}
public void setAllType ( AllTypes allType ) {
this.allType = allType;
}
public byte getMyByte () {
return myByte;
}
public void setMyByte ( byte myByte ) {
this.myByte = myByte;
}
public int getMyInt () {
return myInt;
}
public void setMyInt ( int myInt ) {
this.myInt = myInt;
}
public boolean isMyBoolean () {
return myBoolean;
}
public void setMyBoolean ( boolean myBoolean ) {
this.myBoolean = myBoolean;
}
public short getMyShort () {
return myShort;
}
public void setMyShort ( short myShort ) {
this.myShort = myShort;
}
public long getMyLong () {
return myLong;
}
public void setMyLong ( long myLong ) {
this.myLong = myLong;
}
public String getString () {
return string;
}
public void setString ( String string ) {
this.string = string;
}
public float getMyFloat () {
return myFloat;
}
public void setMyFloat ( float myFloat ) {
this.myFloat = myFloat;
}
public double getMyDouble () {
return myDouble;
}
public void setMyDouble ( double myDouble ) {
this.myDouble = myDouble;
}
public BigDecimal getBigDecimal () {
return bigDecimal;
}
public void setBigDecimal ( BigDecimal bigDecimal ) {
this.bigDecimal = bigDecimal;
}
public BigInteger getBigInteger () {
return bigInteger;
}
public void setBigInteger ( BigInteger bigInteger ) {
this.bigInteger = bigInteger;
}
public Date getDate () {
return date;
}
public void setDate ( Date date ) {
this.date = date;
}
public FooEnum getFoo () {
return foo;
}
public void setFoo ( FooEnum foo ) {
this.foo = foo;
}
public FooEnum getBar () {
return bar;
}
public void setBar ( FooEnum bar ) {
this.bar = bar;
}
@Override
public boolean equals ( Object o ) {
if ( this == o ) return true;
if ( !( o instanceof AllTypes ) ) return false;
AllTypes allTypes1 = ( AllTypes ) o;
if ( myBoolean != allTypes1.myBoolean ) return false;
if ( myByte != allTypes1.myByte ) return false;
if ( Double.compare ( allTypes1.myDouble, myDouble ) != 0 ) return false;
if ( Float.compare ( allTypes1.myFloat, myFloat ) != 0 ) return false;
if ( myInt != allTypes1.myInt ) return false;
if ( myLong != allTypes1.myLong ) return false;
if ( myShort != allTypes1.myShort ) return false;
if ( bigDecimal != null ? !bigDecimal.equals ( allTypes1.bigDecimal ) : allTypes1.bigDecimal != null )
return false;
if ( bigInteger != null ? !bigInteger.equals ( allTypes1.bigInteger ) : allTypes1.bigInteger != null )
return false;
if ( string != null ? !string.equals ( allTypes1.string ) : allTypes1.string != null ) return false;
if ( string2 != null ? !string2.equals ( allTypes1.string2 ) : allTypes1.string2 != null ) return false;
if (allTypes == null && allTypes1.allTypes.size () == 0) {
return true;
} else {
if ( allTypes != null ? !allTypes.equals ( allTypes1.allTypes ) : allTypes1.allTypes != null ) return false;
}
if ( date != null && allTypes1.date!=null) {
long delta = Math.abs ( date.getTime () - allTypes1.date.getTime ());
if ( delta < 1000) {
return true;
} else {
return false;
}
}
if ( allType != null ? !allType.equals ( allTypes1.allType ) : allTypes1.allType != null ) return false;
if ( bar != allTypes1.bar ) return false;
if ( foo != allTypes1.foo ) return false;
return true;
}
@Override
public int hashCode () {
int result;
long temp;
result = myInt;
result = 31 * result + ( myBoolean ? 1 : 0 );
result = 31 * result + ( int ) myShort;
result = 31 * result + ( int ) ( myLong ^ ( myLong >>> 32 ) );
result = 31 * result + ( string != null ? string.hashCode () : 0 );
result = 31 * result + ( string2 != null ? string2.hashCode () : 0 );
result = 31 * result + ( bigDecimal != null ? bigDecimal.hashCode () : 0 );
result = 31 * result + ( bigInteger != null ? bigInteger.hashCode () : 0 );
result = 31 * result + ( date != null ? date.hashCode () : 0 );
result = 31 * result + ( myFloat != +0.0f ? Float.floatToIntBits ( myFloat ) : 0 );
temp = Double.doubleToLongBits ( myDouble );
result = 31 * result + ( int ) ( temp ^ ( temp >>> 32 ) );
result = 31 * result + ( int ) myByte;
result = 31 * result + ( foo != null ? foo.hashCode () : 0 );
result = 31 * result + ( bar != null ? bar.hashCode () : 0 );
result = 31 * result + ( allType != null ? allType.hashCode () : 0 );
result = 31 * result + ( allTypes != null ? allTypes.hashCode () : 0 );
return result;
}
@Override
public String toString () {
return "AllTypes{" +
"myInt=" + myInt +
", myBoolean=" + myBoolean +
", myShort=" + myShort +
", myLong=" + myLong +
", string='" + string + '\'' +
", string2='" + string2 + '\'' +
", bigDecimal=" + bigDecimal +
", bigInteger=" + bigInteger +
", date=" + date +
", myFloat=" + myFloat +
", myDouble=" + myDouble +
", myByte=" + myByte +
", foo=" + foo +
", bar=" + bar +
", allType=" + allType +
", allTypes=" + allTypes +
'}';
}
}
You customize the settings via the factories. You can customize if the parser and serializer uses fields, properties or both. You can also customize how the parser should parser the data (strict or relaxed or even ASCII PLIST style).
JsonParserFactory jsonParserFactory = new JsonParserFactory()
.useFieldsFirst().useFieldsOnly().usePropertiesFirst().usePropertyOnly() //one of these
.plistStyle() //allow parsing of ASCII PList style files
.lax() //allow loose parsing of JSON like JSON Smart
.strict() //opposite of lax
.setCharset( StandardCharsets.UTF_8 ) //Set the standard charset, defaults to UTF_8
.setChop( true ) //chops up buffer overlay buffer (more discussion of this later)
.setLazyChop( true ) //similar to chop but only does it after map.get
;
JsonSerializerFactory jsonSerializerFactory = new JsonSerializerFactory()
.useFieldsFirst().useFieldsOnly().usePropertiesFirst().usePropertyOnly() //one of these
//.addPropertySerializer( ) customize property output
//.addTypeSerializer( ) customize type output
.useJsonFormatForDates() //use json dates
//.addFilter( ) add a property filter to exclude properties
.includeEmpty().includeNulls().includeDefaultValues() //override defaults
.handleComplexBackReference() //uses identity map to track complex back reference and avoid them
.setHandleSimpleBackReference( true ) //looks for simple back reference for parent
.setCacheInstances( true ) //turns on caching for immutable objects
;
Once you have the factories you can create parsers, serializers or object mappers with them as follows:
//You can use parser and serializer directly.
final JsonParser jsonParser = jsonParserFactory.create();
final JsonSerializer jsonSerializer = jsonSerializerFactory.create();
File file = File.createTempFile( "userList", ".json" );
String jsonString = jsonSerializer.serialize( users ).toString();
IO.write( IO.path( file.toString()), jsonString);
List<User> users2 = jsonParser.parseListFromFile( User.class, file.toString() );
// Or you can pass them to the ObjectMapper interface you know and love,
// just pass the factories to it.
ObjectMapper mapper = JsonFactory.create(jsonParserFactory, jsonSerializerFactory);
mapper.writeValue( file, users );
List<User> userList = mapper.readValue( file, List.class, User.class );
puts (userList);
puts ( mapper.writeValueAsString( userList ) );
Boon is the all around fastest JSON parser out of GSON, Jackson and JsonSmart (so far). Boon now has input stream, reader, byte[], char[], CharSequence and String support.
Run on 17" 2011 Mac Book Pro with SSD and 16 GB or RAM.
Testing against a 1.7 MB string.
java -jar target/microbenchmarks.jar ".*string.*Catalog" -wi 3 -i 5 -f 1 -t 8
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.StatelessBoonBenchMark.citmCatalog thrpt 8 5 1 979.087 234.875 ops/s
i.g.j.s.BoonBenchmark.citmCatalog thrpt 8 5 1 975.467 155.524 ops/s
i.g.j.s.BoonClassicBenchmark.citmCatalog thrpt 8 5 1 724.397 76.153 ops/s
i.g.j.s.JsonSmartBenchmark.citmCatalog thrpt 8 5 1 423.947 45.707 ops/s
i.g.j.s.GSONBenchmark.citmCatalog thrpt 8 5 1 373.203 38.523 ops/s
i.g.j.s.JacksonASTBenchmark.citmCatalog thrpt 8 5 1 269.490 30.313 ops/s
i.g.j.s.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 263.187 95.644 ops/s
Boon is 3x+ some of the competitors.
Jackson did so poorly that I ran it again by itself.
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.JacksonASTBenchmark.citmCatalog thrpt 8 5 1 314.703 96.497 ops/s
i.g.j.s.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 381.953 11.466 ops/s
Then to be fair to boon, I ran the two top performing Boon parser configurations as well
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.BoonBenchmark.citmCatalog thrpt 8 5 1 1249.423 215.237 ops/s
i.g.j.s.StatelessBoonBenchMark.citmCatalog thrpt 8 5 1 1074.957 255.704 ops/s
Then the run off test:
Fastest Jackson against Fastest Boon
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.BoonBenchmark.citmCatalog thrpt 8 5 1 1306.067 75.496 ops/s
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 369.557 16.925 ops/s
Boon maintains its 4x status over Jackson for this test. citmCatalog is a 1.7 MB file.
Boon is 4x faster when you run them by themselves.
Testing against a 2K JSON file
java -jar target/microbenchmarks.jar ".*string.*medium" -wi 3 -i 5 -f 3 -t 8
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.StatelessBoonBenchMark.medium thrpt 8 15 1 828614.167 113309.263 ops/s
i.g.j.s.BoonBenchmark.medium thrpt 8 15 1 655723.022 113194.181 ops/s
i.g.j.s.BoonClassicBenchmark.medium thrpt 8 15 1 544726.818 31969.515 ops/s
i.g.j.s.JsonSmartBenchmark.medium thrpt 8 15 1 261257.857 15178.567 ops/s
i.g.j.s.JacksonASTBenchmark.medium thrpt 8 15 1 244845.260 4459.778 ops/s
i.g.j.s.GSONBenchmark.medium thrpt 8 15 1 242176.437 5552.595 ops/s
i.g.j.s.JacksonObjectBenchmark.medium thrpt 8 15 1 239011.644 9544.070 ops/s
Boon is nearly 4x faster than Jackson in this configuration.
Now lets run the fastest boon against the fastest Jackson by themselves.
java -jar target/microbenchmarks.jar ".*string.*JacksonASTBenchmark.*medium" -wi 3 -i 5 -f 1 -t 8
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.JacksonASTBenchmark.medium thrpt 8 5 1 285827.937 30910.474 ops/s
Now the fastest Boon
java -jar target/microbenchmarks.jar ".*string.*BoonBenchmark.*medium" -wi 3 -i 5 -f 1 -t 8
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.BoonBenchmark.medium thrpt 8 5 1 1053372.327 16233.011 ops/s
The conclusion from 12/15/2013 to 1/4/2014 Boon is 100% faster than Boon, and where it was 50% to 100% faster than Jackson. Boon is now 200%, 300% and sometimes up to 400% faster than Jackson.
Boon wins by large margins when it comes to String parsing.
How does Boon do against the competitors when it is a byte[] instead of a String?
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.b.BoonBenchmark.medium thrpt 8 5 1 618123.037 29518.544 ops/s
i.g.j.b.JacksonObjectBenchmark.medium thrpt 8 5 1 379202.950 22999.872 ops/s
i.g.j.b.JacksonASTBenchmark.medium thrpt 8 5 1 293054.527 23981.011 ops/s
i.g.j.b.GSONBenchmark.medium thrpt 8 5 1 205735.037 207194.930 ops/s
i.g.j.b.JsonSmartBenchmark.medium thrpt 8 5 1 250384.043 25851.296 ops/s
Jackson does much better on this test than GSON and JsonSmart, but not better than Boon.
Now lets run them by themselves.
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.b.BoonBenchmark.medium thrpt 8 5 1 827191.353 19862.004 ops/s
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.b.JacksonObjectBenchmark.medium thrpt 8 5 1 354519.703 28758.081 ops/s
Boon is over 2x faster at parsing byte arrays then Jackson is. (Boon has about 5 different ways to parse a byte[] and all are faster than Jackson.)
Jackson on all files from json.org.
java -jar target/microbenchmarks.jar ".*bytes.JacksonObjectBenchmark." -wi 3 -i 5 -f 1 -t 8
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.b.JacksonObjectBenchmark.actionLabel thrpt 8 5 1 320460.340 72131.986 ops/s
i.g.j.b.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 268.130 55.186 ops/s
i.g.j.b.JacksonObjectBenchmark.medium thrpt 8 5 1 197295.940 22306.259 ops/s
i.g.j.b.JacksonObjectBenchmark.menu thrpt 8 5 1 976004.950 268554.107 ops/s
i.g.j.b.JacksonObjectBenchmark.sgml thrpt 8 5 1 572307.433 163097.782 ops/s
i.g.j.b.JacksonObjectBenchmark.small thrpt 8 5 1 2275296.777 304454.266 ops/s
i.g.j.b.JacksonObjectBenchmark.webxml thrpt 8 5 1 111176.677 19523.744 ops/s
i.g.j.b.JacksonObjectBenchmark.widget thrpt 8 5 1 343268.823 119559.082 ops/s
Boon all files on JSON.org
java -jar target/microbenchmarks.jar ".*bytes.BoonBenchmark." -wi 3 -i 5 -f 1 -t 8
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.b.BoonBenchmark.actionLabel thrpt 8 5 1 618993.350 48683.336 ops/s
i.g.j.b.BoonBenchmark.citmCatalog thrpt 8 5 1 492.307 80.017 ops/s
i.g.j.b.BoonBenchmark.medium thrpt 8 5 1 458555.533 10036.229 ops/s
i.g.j.b.BoonBenchmark.menu thrpt 8 5 1 1959464.230 505004.028 ops/s
i.g.j.b.BoonBenchmark.sgml thrpt 8 5 1 1300516.190 59998.099 ops/s
i.g.j.b.BoonBenchmark.small thrpt 8 5 1 10017411.120 171608.588 ops/s
i.g.j.b.BoonBenchmark.webxml thrpt 8 5 1 253177.177 141918.931 ops/s
i.g.j.b.BoonBenchmark.widget thrpt 8 5 1 1045186.440 95933.625 ops/s
i.g.j.b.BoonBenchmark.actionLabel thrpt 8 5 1 618993.350 48683.336 ops/s
i.g.j.b.JacksonObjectBenchmark.actionLabel thrpt 8 5 1 320460.340 72131.986 ops/s
Boon nearly 2x faster
1.7 MB
i.g.j.b.BoonBenchmark.citmCatalog thrpt 8 5 1 492.307 80.017 ops/s
i.g.j.b.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 268.130 55.186 ops/s
Boon nearly 2x faster
i.g.j.b.JacksonObjectBenchmark.small thrpt 8 5 1 2275296.777 304454.266 ops/s
i.g.j.b.BoonBenchmark.small thrpt 8 5 1 10017411.120 171608.588 ops/s
Boon nearly 5x faster!
i.g.j.b.JacksonObjectBenchmark.widget thrpt 8 5 1 343268.823 119559.082 ops/s
i.g.j.b.BoonBenchmark.widget thrpt 8 5 1 1045186.440 95933.625 ops/s
Boon almost 3x faster!
i.g.j.b.BoonBenchmark.medium thrpt 8 5 1 458555.533 10036.229 ops/s
i.g.j.b.JacksonObjectBenchmark.medium thrpt 8 5 1 197295.940 22306.259 ops/s
Boon over 2x faster!
i.g.j.b.JacksonObjectBenchmark.menu thrpt 8 5 1 976004.950 268554.107 ops/s
i.g.j.b.BoonBenchmark.menu thrpt 8 5 1 1959464.230 505004.028 ops/s
Boon over 2x faster!
Winner overall for byte[]. Boon KILLS IT! Sweep!
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.inputStream.BoonBenchmark.actionLabel thrpt 8 5 1 168598.707 10702.291 ops/s
i.g.j.inputStream.JacksonObjectBenchmark.actionLabel thrpt 8 5 1 165700.530 7939.486 ops/s
Boon barely wins
i.g.j.inputStream.BoonBenchmark.citmCatalog thrpt 8 5 1 555.373 50.952 ops/s
i.g.j.inputStream.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 285.610 66.244 ops/s
Boon wins by 2x
i.g.j.inputStream.BoonBenchmark.medium thrpt 8 5 1 159105.320 19057.916 ops/s
i.g.j.inputStream.JacksonObjectBenchmark.medium thrpt 8 5 1 122754.690 130516.150 ops/s
Boon wins
i.g.j.inputStream.JacksonObjectBenchmark.menu thrpt 8 5 1 252556.270 179084.546 ops/s
i.g.j.inputStream.BoonBenchmark.menu thrpt 8 5 1 184977.057 5153.660 ops/s
Jackson wins
i.g.j.inputStream.JacksonObjectBenchmark.sgml thrpt 8 5 1 193714.667 3526.321 ops/s
i.g.j.inputStream.BoonBenchmark.sgml thrpt 8 5 1 180970.150 3499.859 ops/s
Jackson barely wins
i.g.j.inputStream.BoonBenchmark.webxml thrpt 8 5 1 132207.987 110279.994 ops/s
i.g.j.inputStream.JacksonObjectBenchmark.webxml thrpt 8 5 1 87252.977 16961.356 ops/s
Boon wins by a really good margin!
i.g.j.inputStream.BoonBenchmark.widget thrpt 8 5 1 178216.637 21779.520 ops/s
i.g.j.inputStream.JacksonObjectBenchmark.widget thrpt 8 5 1 171787.453 28138.131 ops/s
Boon wins
Boon wins nearly all, but Jackson wins a few here. GSON also does well at inputStream. Winner overall for inputStream. Boon!
Benchmark Mode Thr Count Sec Mean Mean error Units
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.r.BoonBenchmark.actionLabel thrpt 8 5 1 186001.097 18425.520 ops/s
i.g.j.r.JacksonObjectBenchmark.actionLabel thrpt 8 5 1 163270.073 3730.813 ops/s
Boon wins!
i.g.j.r.BoonBenchmark.citmCatalog thrpt 8 5 1 530.150 108.657 ops/s
i.g.j.r.JacksonObjectBenchmark.citmCatalog thrpt 8 5 1 226.990 81.640 ops/s
Boon wins by a large margin! Close to 2x
i.g.j.r.BoonBenchmark.medium thrpt 8 5 1 136242.553 4719.231 ops/s
i.g.j.r.JacksonObjectBenchmark.medium thrpt 8 5 1 105882.313 3083.148 ops/s
Boon wins by 30%!
i.g.j.r.BoonBenchmark.menu thrpt 8 5 1 145418.360 19145.101 ops/s
i.g.j.r.JacksonObjectBenchmark.menu thrpt 8 5 1 134975.063 7718.413 ops/s
Boon wins by a tad!
i.g.j.r.BoonBenchmark.sgml thrpt 8 5 1 128216.103 141559.091 ops/s
i.g.j.r.JacksonObjectBenchmark.sgml thrpt 8 5 1 140934.190 123224.172 ops/s
Jackson wins!
i.g.j.r.JacksonObjectBenchmark.small thrpt 8 5 1 145166.813 3960.731 ops/s
i.g.j.r.BoonBenchmark.small thrpt 8 5 1 145121.580 19487.609 ops/s
Almost a tie, but Jackson wins.
i.g.j.r.BoonBenchmark.webxml thrpt 8 5 1 116019.730 10943.316 ops/s
i.g.j.r.JacksonObjectBenchmark.webxml thrpt 8 5 1 71660.963 43656.284 ops/s
Boon wins by a large margin about 35%.
i.g.j.r.BoonBenchmark.widget thrpt 8 5 1 143853.053 11396.247 ops/s
i.g.j.r.JacksonObjectBenchmark.widget thrpt 8 5 1 119811.283 14264.563 ops/s
Boon wins by a large margin about 30%
In two instances Jackson barely beats boon. In one instance Jackson wins by 15%. Boon wins by 200%, 35%, 30% and 30%. Then there are a few smaller wins for Boon.
Winner overall for reader. Boon!
Jackson
Benchmark Mode Thr Count Sec Mean Mean error Units
i.g.j.s.BoonSerializer.roundTriper thrpt 8 5 1 214115.270 64366.819 ops/s
i.g.j.s.JacksonSerializer.roundTriper thrpt 8 5 1 96239.373 91586.021 ops/s
Boon over 2x faster!
i.g.j.s.BoonSerializer.serializeSmall thrpt 8 5 1 692168.577 70292.894 ops/s
i.g.j.s.JacksonSerializer.serializeSmall thrpt 8 5 1 436992.523 68051.334 ops/s
Boon is substantially faster!
Boon wins the object serialization battle.
Boon allows fields to be included or ignored if a view is active.
Returning to our User example. Let's say you have a user with the following empId that may or may not be active based on a view as follows:
public static class User {
@JsonViews( ignoreWithViews = {"public"},
includeWithViews = {"internal"})
private String empId = "555-55-5555";
To activate the inclusion of empId field, you activate the public view. No active view means all fields will be active with JsonViews. To filter a field from a view, you must activate a view.
Here is an example and some sample output to show how this works:
final User rick = BeanUtils.copy( user );
rick.getName().setFirst( "Rick" );
boolean ok = true;
JsonSerializer serializer = new JsonSerializerFactory().useAnnotations().setView( "public" ).create();
String str = serializer.serialize( rick ).toString();
puts (str);
ok |= !str.contains( "\"empId\":" ) || die(str);
Here we serialize using the public view so as expected, we do not get the empId
{"status":null,"gender":"MALE","name":{"first":"Rick","last":"Hightower"},"verified":true,"birthDate":328147200024}
Next we serialize with the internal view so as expected we do get the empId.
serializer = new JsonSerializerFactory().useAnnotations().setView( "internal" ).create();
str = serializer.serialize( rick ).toString();
ok |= str.contains( "\"empId\":" ) || die(str);
puts (str);
Here is the empId as expected:
{"empId":"555-55-5555","status":null,"gender":"MALE","name":{"first":"Rick","last":"Hightower"},"verified":true,"birthDate":328147200024}
No active view means, all fields are not filtered by @JsonView as follows:
serializer = new JsonSerializerFactory().useAnnotations().create();
str = serializer.serialize( rick ).toString();
ok |= str.contains( "\"empId\":" ) || die(str);
puts (str);
Since we did not have a view, then the empId was not filtered.
Let me break this down a little. If you did not get it yet...
To activate a view you do this:
serializer = new JsonSerializerFactory().useAnnotations().setView( "internal" ).create();
The above creates a serializer for the view names "internal".
The developer uses the @JsonViews annotations to turn on or turn off views.
public static class User {
@JsonViews( ignoreWithViews = {"public"},
includeWithViews = {"internal"})
private String empId = "555-55-5555";
The above says: turn off this field if the serializer is public, but include it if the view is internal.
Since we used the internal view name when we created the serializer, it will not put the empId in the serialization.
If you use a view name, as this is written, it will only output empId if the view name is internal.
ignoreWithViews and includeWithViews take a list of view names so a field can be written out with multiple views and excluded from multiple views.
Your view names could be user-listing, user-form, etc. The idea being, I want to use the same DTO but I don't always want all of the fields sent.
To exclude the empId, you would create the serializer as follows:
JsonSerializer serializer = new JsonSerializerFactory().useAnnotations().setView( "public" ).create();
String str = serializer.serialize( rick ).toString();
Boon knows how to deal with subclasses, interfaces and abstract classes without intervention for proper field serialization. It just does the right thing.
Check out the following example:
public class Person {
public Pet pet;
public Animal pet2;
…
}
public interface Animal {
void name(String name);
}
package org.boon.json.test;
public abstract class Pet implements Animal{
public String name;
public void name (String name) {
this.name = name;
}
}
public class Dog extends Pet implements Animal{
public boolean barks;
}
Now you might think to handle the above you need a lot of annotations and you may even need to read some additional documentation, but no! It just works. Demonstrated as follows:
Person person = new Person();
person.name = "Rick";
person.city = "Tucson";
person.pet = new Dog ();
person.pet2 = new Dog ();
person.pet.name = "Mooney";
person.pet2.name ( "Annabel" );
((Dog)person.pet).barks = true;
ObjectMapper mapper = JsonFactory.createUseAnnotations( true );
puts ( mapper.toJson ( person ) );
Map <String, Object> map = ( Map<String, Object> ) mapper.fromJson ( mapper.toJson ( person ), Map.class );
Map <String, Object> petMap = ( Map<String, Object> ) map.get ( "pet" );
Map <String, Object> pet2Map = ( Map<String, Object> ) map.get ( "pet" );
String className = (String)petMap.get("class");
boolean ok = className.endsWith ( ".Dog" ) || die(className);
className = (String)pet2Map.get("class");
ok = className.endsWith ( ".Dog" ) || die(className);
Person person2 = mapper.fromJson ( mapper.toJson ( person ), Person.class );
Dog dog = (Dog)person2.pet;
ok = dog.name.equals ( "Mooney" ) || die( dog.name );
dog = (Dog)person2.pet2;
ok = dog.name.equals ( "Annabel" ) || die( dog.name );
Less fiddling with annotations. More getting stuff done.
##Using annotations to customize serialization property
If you want to change the name of a property, you can use the following:
public class Person {
@JsonProperty ( "moniker" )
public String name;
@SerializedName ( "homeTown" )
public String city;
@JsonProperty is a Jackson style annotation. @SerializedName is a Gson style annotation. You can use either one, they do the same thing, change the way the object is written and read from JSON.
package com.examples;
import org.boon.Maps;
import org.boon.json.JsonParserAndMapper;
import org.boon.json.JsonParserFactory;
import java.util.List;
import java.util.Map;
import static org.boon.Boon.puts;
import static org.boon.Exceptions.die;
import static org.boon.Lists.list;
import static org.boon.Maps.copy;
import static org.boon.Maps.fromMap;
import static org.boon.json.JsonFactory.fromJson;
import static org.boon.json.JsonFactory.toJson;
/**
* Created by Richard on 5/1/14.
*/
public class ValidatePropsArePresent {
public static class Employee {
String firstName;
String lastName;
List<String> todo;
public Employee(String firstName, String lastName, List<String> todo) {
this.firstName = firstName;
this.lastName = lastName;
this.todo = todo;
}
@Override
public String toString() {
return "Employee{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", todo=" + todo +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee employee = (Employee) o;
if (firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) return false;
if (lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null) return false;
if (todo != null ? !todo.equals(employee.todo) : employee.todo != null) return false;
return true;
}
@Override
public int hashCode() {
int result = firstName != null ? firstName.hashCode() : 0;
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
result = 31 * result + (todo != null ? todo.hashCode() : 0);
return result;
}
}
public static void main(String... args) {
//Happy case
Employee hovik = new Employee("Hovik", "Gambino",
list("Eat Salad", "Complain about US politics"));
String json = toJson(hovik);
puts( "hovik json", json );
puts( "hovik object", hovik );
Employee hovik2 = fromJson(json, Employee.class);
boolean ok = hovik.equals(hovik2) || die("Hovik is not hovik");
JsonParserAndMapper mapper = new JsonParserFactory().strict().create();
// Missing todos
Map<String, Object> map = mapper.parseMap("{\"firstName\":\"Hovik\",\"lastName\":\"Gambino\"} ");
//Validate
boolean hasTodos = map.containsKey("todo");
if (hasTodos) {
puts ("It has todo");
} else {
puts ("No todos!!!!");
}
//You can still convert to an object, in fact there is almost no overhead, the map is not a real map it is an parsed index overlay that looks like a map :)
Employee hovik3WithNoTodos = fromMap(map, Employee.class);
puts ("hovik3WithNoTodos", hovik3WithNoTodos);
//You can manipulate the map too
map = copy(map);
map.put("todo", list("Read Scala Book", "Learn Boon", "Learn Vertx and Groovy",
"Buy Rick Lunch", "Buy Rick Beer"));
hovik3WithNoTodos = fromMap(map, Employee.class);
puts ("hovik3WithTodos!", hovik3WithNoTodos);
}
}
Thoughts? Write me at richard high tower AT g mail dot c-o-m (Rick Hightower).
If you are new to boon start here:
- Java Boon Byte Buffer Builder
- Java Boon Slice Notation
- Java Boon Slice's work with TreeSets
- Java Boon Description
- More...
- Boon Home
- Boon Source
- Introducing Boon October 2013
- Java Slice Notation
- What if Java collections were easy to search and sort?
- Boon HTTP utils
- Boon Java JSON parser Benchmarks or hell yeah JSON parsing is damn fast!
- Boon JSON parser is really damn fast! Part II
- Boon JSON parser Round III now just not fast as but much faster than other Java JSON parsers
- Boon World's fastest Java JSON parser Round IV from fast to blazing to rocket fuel aka Braggers going to brag
- Boon gets adopted by JSON Path as the default Java JSON parser
- Boon graphics showing just how fast Boon JSON parsing is - about 50% to 200% faster than the graphs shown here now so wicked fast became wickeder - just got sick of making graphics
- 10 minute guide to Boon JSON parsing after I added @JsonIgnore, @JsonProperty, @JsonView, @Exposes, etc.
- Hightower speaks to the master of Java JSON parsing, the king of speed The COW TOWN CODER!
- Boon provides easy Java objects from lists, from maps and from JSON.
Easily read in files into lines or a giant string with one method call. Works with files, URLs, class-path, etc. Boon IO support will surprise you how easy it is. Boon has Slice notation for dealing with Strings, Lists, primitive arrays, Tree Maps, etc. If you are from Groovy land, Ruby land, Python land, or whatever land, and you have to use Java then Boon might give you some relief from API bloat. If you are like me, and you like to use Java, then Boon is for you too. Boon lets Java be Java, but adds the missing productive APIs from Python, Ruby, and Groovy. Boon may not be Ruby or Groovy, but its a real Boon to Java development.
Core Boon will never have any dependencies. It will always be able to run as a single jar. This is not just NIH, but it is partly. My view of what Java needs is more inline with what Python, Ruby and Groovy provide. Boon is an addition on top of the JVM to make up the difference between the harder to use APIs that come with Java and the types of utilities that are built into Ruby, Python, PHP, Groovy etc. Boon is a Java centric view of those libs. The vision of Boon and the current implementation is really far apart.
===
Contact Info
blog|[twitter](https://twitter.com/RickHigh|[infoq]http://www.infoq.com/author/Rick-Hightower|[stackoverflow](http://stackoverflow.com/users/2876739/rickhigh)|[java lobby](http://java.dzone.com/users/rhightower)|Other | richard high tower AT g mail dot c-o-m (Rick Hightower)|work|cloud|nosql
YourKit supports Boon open source project with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products: YourKit Java Profiler and YourKit .Net profiler.