A sweet little JSON serializer/deserializer ideal for personal projects.
- Quick and dirty implementation. Don't expect 100% spec compliance or off-the-charts performance.
- Development in progress.
- Serialization not supported yet.
Note: None of the methods described below are thread safe. Concurrency must be manually handled.
data.json
{
"people": [
{
"firstname": "John",
"lastname": "Doe",
"skills": ["java", "c", "c++", "sql"]
},
{
"firstname": "Jane",
"lastname": "Doe",
"skills": ["javascript", "python", "rust"]
}
]
}
Main.java
public class Main {
public static void main(String[] args) {
var json = JsonParser.parse(Paths.get("data.json")).as_map();
for (JsonValue value : json.get("people").as_list()) {
var person = value.as_map();
System.out.println(person.get("firstname").as_string());
System.out.println(person.get("lastname").as_string());
System.out.println(person.get("skills").as_list().get(0).as_string());
}
}
}
The parse
method returns a JsonValue
which can be mapped to a data model by calling the bind_to
method on
it. Partial bindings are also supported.
data.json
{
"user_id": 123456,
"firstname": "John",
"lastname": "Doe"
}
Main.java
class User {
private int user_id;
private String firstname;
private String lastname;
public String get_firstname () { return firstname; }
// ...
}
public class Main {
public static void main (String[] args) throws IOException {
var json = Files.readString(Paths.get("data.json"));
var user = JsonParser.parse(json).bind_to(User.class);
System.out.println(user.get_firstname()); // prints "John"
}
}
Primitive types as well as parameterized and array types are supported. Note that custom annotations aren't
required (will be added in the future, hopefully soon). The binder will write values to field whose name
corresponds to a field in the JSON string. Furthermore, the writer will leave out transient and inherited fields.
JSON fields that are null
are by default ignored and there is no way to change that behavior as of now. Extra
fields in the JSON string are skipped if there are no members corresponding to that key.
We can specify custom binders to handle mapping to objects of types that do not conform to the structure of JSON data:
public class Main {
public static void main (String[] args) throws IOException {
var json = Files.readString(Paths.get("data.json"));
// Register a binder for `java.util.List`.
// `value` is the JsonValue that we are dealing with.
// `typedef` describes the type of the variable to which the JsonValue will be bound.
// `bag` contains data that maybe useful to the binder (such as hints for implementation
// to be used as the model for this binding).
SweetJson.register_binder(Typedef.wrap(List.class), (value, typedef, bag) -> {
var model = new ArrayList<>();
var arg = typedef.type_arg1();
var list = value.as_list();
list.forEach(entry -> model.add(entry.bind_to(arg)));
return model;
});
var user = JsonParser.parse(json).bind_to(User.class);
// Assuming `User` has a `List` named `skills`...
System.out.println(user.skills.get(0));
}
}
Here we are hardcoding ArrayList
as the container. If that's not acceptable, you can insert data into the bag
and
inspect it before creating a collection instance. The data stored in the bag
is guaranteed to persist throughout the
lifetime of a binding request.
SweetJson provides basic deserialization support for generic types. Here's a (bad) example:
{
"user": {
"name": "John Doe",
"alias": "doejohn",
"email": "johndoe@example.com"
}
}
class Employee {
private String name;
private String alias;
private String email;
public void print_alias () {
System.out.println(alias);
}
}
class Server <T> {
private T user;
// ...more fields
public T get_user() {
return user;
}
}
@SuppressWarnings("unchecked")
public class Main {
public static void main (String[] args) {
var json_value = JsonParser.parse(Paths.get("data.json"));
// bind_to_generic(Prototype.class, Typearg1.class...TypeargN.class)
var server = json_value.bind_to_generic(Server.class, Employee.class);
var employee = (Employee)server.get_user();
employee.print_alias(); // Prints "doejohn"
}
}
Nested parameterized types, such as List<MyParameterizedType<String>>
are not supported yet.
Note: Documentation is updated from time to time as the development proceeds. But the README usually won't reflect the latest API changes.