ActiveSerializer Examples

Note: You have two options to run the examples.
Option 1: Create a new project, import ActiveSerializer Maven repository and recreate the examples from scratch.
Option 2: clone ActiveJ project from GitHub ($ git clone https://github.com/activej/activej)
and import it as a Maven project. Check out branch master and build the project. The examples are located at activej -> examples -> core -> serializer

Simple Object Serialization

In order to create classes whose instances can be serialized/deserialized, you should use special annotations:

  • @Serialize annotation with order number on property getter. Parameter order provides better compatibility in case classes are changed.
  • @Deserialize annotation with property name (which should be the same name as the one in getter) in constructor.

This is enough to create serializable POJOs, for example:

public static class Person {
	public Person(@Deserialize("age") int age,
				  @Deserialize("name") String name) {
		this.age = age;
		this.name = name;
	}

	@Serialize(order = 0)
	public final int age;

	@Serialize(order = 1)
	public final String name;

	private String surname;

	@Serialize(order = 2)
	public String getSurname() {
		return surname;
	}

	public void setSurname(String surname) {
		this.surname = surname;
	}
}

Now let’s do some serialization. We’ll create a Person instance, a byte array that stores the result of the serialization, and a BinarySerializer instance that represents a serializer that encodes and decodes <T> values to byte arrays (<Person> values in this case):

Person john = new Person(34, "Jim");
john.setSurname("Smith");
byte[] buffer = new byte[200];
BinarySerializer<Person> serializer = SerializerBuilder.create()
		.build(Person.class);

That’s it, now we can serialize and deserialize our Person instance:

serializer.encode(buffer, 0, john);
Person johnCopy = serializer.decode(buffer, 0);

Let’s make a simple test to check if everything works correctly:

System.out.println(john.age + " " + johnCopy.age);
System.out.println(john.name + " " + johnCopy.name);
System.out.println(john.getSurname() + " " + johnCopy.getSurname());

After you run the example, you’ll receive the following output:

34 34
Jim Jim
Smith Smith

Which means that the serialization and deserialization worked correctly.

You can explore full example sources on GitHub.

Generics and Interfaces Serialization

ActiveSerializer can simply manage more complex objects. For example, let’s see how it works with interfaces and generics.

First, create a simple Skill class:

public static class Skill<K, V> {
	private final K key;
	private final V value;

	public Skill(@Deserialize("key") K key,
				 @Deserialize("value") V value) {
		this.key = key;
		this.value = value;
	}

	@Serialize(order = 0)
	public K getKey() {
		return key;
	}

	@Serialize(order = 1)
	public V getValue() {
		return value;
	}
}

Next, create a Person interface that has a single method returning a list of skills:

public interface Person<K, V> {
	@Serialize(order = 0)
	List<Skill<K, V>> getSkills();
}

Finally create a Developer class that implements Person interface:

public static class Developer implements Person<Integer, String> {
	private List<Skill<Integer, String>> list;

	@Serialize(order = 0)
	@Override
	public List<Skill<Integer, String>> getSkills() {
		return list;
	}

	public void setSkills(List<Skill<Integer, String>> list) {
		this.list = list;
	}
}

Let’s proceed to the serialization. Similarly to the previous example, we’ll create an instance of the Developer, a byte array to store the result of the serialization and an instance of BinarySerializer<Developer> serializer:

Developer developer = new Developer();
developer.setSkills(Arrays.asList(
		new Skill<>(1, "Java"),
		new Skill<>(2, "ActiveJ")));

byte[] buffer = new byte[200];
BinarySerializer<Developer> serializer = SerializerBuilder.create()
		.build(Developer.class);

Now let’s serialize and deserialize our Developer instance:

serializer.encode(buffer, 0, developer);
Developer developer2 = serializer.decode(buffer, 0);

Check if the serialization works correctly:

for (int i = 0; i < developer.getSkills().size(); i++) {
	System.out.println(developer.getSkills().get(i).getKey() + " - " + developer.getSkills().get(i).getValue() +
			", " + developer2.getSkills().get(i).getKey() + " - " + developer2.getSkills().get(i).getValue());
}

If you run the example, you’ll receive the following output:

1 - Java, 1 - Java
2 - ActiveJ, 2 - ActiveJ

Which means that the serialization worked correctly.

You can explore full example sources on GitHub.

Fixed Size and Nullable Fields Serialization

ActiveSerializer has some helper annotations, for example:

  • @SerializeNullable on properties that can have null values. This annotation also has a special path parameter. It represent a path of the tree of the variable’s data types. It allows to indicate which of the ‘nodes’ is nullable.

For example:

@Serialize(order = 0)
@SerializeNullable() // refers to Map<String, Map<Integer, Float[]>>
@SerializeNullable(path = {1}) // refers to Map<Integer, Float[]
@SerializeNullable(path = {0}) // refers to String
@SerializeNullable(path = {1, 0}) // refers to Integer
@SerializeNullable(path = {1, 1}) // refers to Float[]
@SerializeNullable(path = {1, 1, 0}) //refers to the Float elements of the array
public Map<String, Map<Integer, Float[]>> complexMap;

As you can see, you can write several annotations for the different paths of the same data structure.

Let’s create a simple example that illustrates how to use these annotations:

public static class Storage {
	@Serialize(order = 0)
	@SerializeFixedSize(3)
	@SerializeNullable(path = {0})
	public String[] strings;

	@Serialize(order = 1)
	@SerializeFixedSize(4)
	public byte[] bytes;
}

Now let’s serialize and deserialize an instance of the Storage similarly to the previous examples. We’ll create an instance of the Storage, a byte array to store the result of the serialization and an instance of BinarySerializer<Storage> serializer:

Storage storage = new Storage();
storage.strings = new String[]{"abc", null, "123", "superfluous"};
storage.bytes = new byte[]{1, 2, 3, 4};

byte[] buffer = new byte[200];
BinarySerializer<Storage> serializer = SerializerBuilder.create()
		.build(Storage.class);

Finally, serialize and deserialize Storage instance:

serializer.encode(buffer, 0, storage);
Storage limitedStorage = serializer.decode(buffer, 0);

Let’s see how serialization affected the storage:

System.out.println(Arrays.toString(storage.strings) + " -> " + Arrays.toString(limitedStorage.strings));
System.out.println(Arrays.toString(storage.bytes) + " -> " + Arrays.toString(limitedStorage.bytes));

If you run the example, you’ll see the following output:

[abc, null, 123, superfluous] -> [abc, null, 123]
[1, 2, 3, 4] -> [1, 2, 3, 4]

As you can see in the first line, storage differs from limitedStorage. This is because @SerializeFixedSize annotation was set at value 3 for the strings property. Thus, “superfluous” was removed from the array while serialization took place.

You can explore full example sources on GitHub.