Default adapter options

Skip null values

Default value: false.


By default, null values are written like any other class field. They can be skipped to save space.

odin.getDefaultAdapterOption().setWritingNull(false);

During the reading process, all absent fields use the default field value only if the class contains an empty constructor, otherwise the field value stays null.

Exceptions

The default object adapter provides some strategy to change the way exceptions are thrown.


Odin has 3 possible exceptions:

  • OdinFormatException


    In the serialisation process, this exception can only be thrown when a key contains an invalid character: =:<>{} [],'"
    In a deserialization process, this exception is thrown when the reader reaches an invalid object character ( wrong object delimiter ( {}[] ), wrong primitive values like non decimal characters, keys with invalid characters or when reading a value instead of a key ).

  • OdinTypeException


    This exception is thrown when trying to register a primitive or an array adapter. In the deserialization, this exception can also be thrown when a type definition refers to an unknown Class or when the class does not contain any empty constructor, and the unsafe instanciation cannot be invoked.

  • OdinAdapterException


    This is the main exception of the library: when writing or reading an object, each sub object runtime exception is rethrown as an OdinAdapterException.
    Globally, you can have OdinFormatException only for primitives and odin elements, and OdinTypeException only when the given object type cannot be built. For any other case, you receive this OdinAdapterException.
    This exception also contains a custom StackTrace. When a printStackTrace is called, the exception logs begin with the path of the exception from the given object towards the object causing the error.


There are 3 strategies that can be changed:

  • Field getting


    This strategy is used if the serialization cannot access the field value or if a field with readTo parameter cannot be accessed ( this exception catch IllegalAccessException )

  • Write


    This strategy is used if a RuntimeException is caught during the writing process

  • Read


    This strategy is used if a RuntimeException is caught during the reading process


The default strategy is ExceptionsStrategy.FORWARD_EXCEPTION

, this strategy forwards the exception in order to stop the whole process.

Other existing strategies are: ExceptionsStrategy.LOG_EXCEPTION that prints the stack trace of the exception in the console and ExceptionsStrategy.CONTINUE_EXECUTION that does nothing.

odin.getDefaultAdapterOption().setReaderExceptionStrategy(ExceptionsStrategy.LOG_EXCEPTION);

You can also create your own strategy by using the ExceptionStrategy interface.

Annotations

After that, Odin comes with some annotations that help you control how the fields are serialized.


Annotations require that the declaring class does not use custom adapter.

Field names and comments

The first annotation is @OdinField. Placing it in a class field allows you to rename the field on the odn output.

This annotation also allows you to add comments before the field in case of an indented output.

@OdinField(name = "player id", comments = "define the player id")
private int id = 5;
# define the player id
player id = 5

The name cannot contain the following characters: =:<>{} [],'" .

If one of these previous characters is present, an OdinFormatException will be thrown.


However, a \ is added before special characters that need to be escaped: \\r \n\t\f\b.


The field comments are Strings arrays, each string represents a line. Use empty Strings to add empty lines to the output.

Custom adapter

To change the adapter of a field without registering an adapter for the type, you can define an @OdinCustomAdapter annotation.

Set as parameter the desired adapter class, and Odin will implement it and use it for this field.



For example you can register a custom adapter to change the way a Map is serialized without registering an adapter for every Map in your model.


In this example, we use this custom Map adapter to only serialize the keys, and read values from another Map on the deserialization.

@OdinCustomAdapter(adapter = FooMapAdapter.class)
private Map<String, Foo> fooMap;
public class FooMapAdapter implements OdinImmutableAdapter<Map<String, Foo>> {
    
    @Override
    public Map<String, Foo> read(OdinReader reader, ObjectType<? extends Map<String, Foo>> type)
            throws IOException {
        Map<String, Foo> result = new HashMap<>();
        while (reader.hasNext()) {
            String id = reader.readString();
            result.put(id, getFoo(id)); // get foo object by it's id
        }
        return result;
    }
    
    @Override
    public void write(OdinWriter writer, ObjectType<? extends Map<String, Foo>> type, Map<String, Foo> o)
            throws IOException {
        for (String id : o.keySet()) {
            writer.writeString(id);
        }
    }
}

The adapter can be one of OdinAdapter implementation: OdinObjectAdapter, OdinImmutableAdapter, OdinInlineAdapter.

Map as a list

Map keys can be repetitive and some data models have the key inside the value, like id or name.


In order to lighten the export and facilitate manual modifications, all of the maps with @OdinMapAsList annotation only write the map values.

During the deserialization, the key is found inside the value with the given keyField.

private Map<String, Foo> elements;
{
    "just a key" : {
        id = "just a key"
        value = "foo"
    }
}
@OdinMapAsList(keyField = "id")
private Map<String, Foo> elements;
{
    {
        id = "just a key"
        value = "foo"
    }
}

Performance-wise, the field needs to be in the value class or superclass. This is not possible to search within a field using a path.

Read to current instance

To tell the Odin default adapter that it must use the readTo method, you need to add the @OdinReadTo annotation.

This way, the field instance doesn't change: the object is read in the previous field value. If the field is null, a new object is instanciated.

@OdinReadTo
private Foo foo;

Field Filter

Since Odin 1.2, you have the possibility to filter the fields accessible from adapters. By default the fields marked as static or transient are ignored.

This means that ignored fields are not accessible inside Odin Types and by this way are not serialized / deserialized.


To change the field filter you can give a custom Predicate<Field> to Odin.


For example, to serialize only non-static field that contains a SerializeField annotation, use the following lambda.

odin.setFieldFilter((Field field) -> !Modifier.isStatic(field.getModifiers()) && field.isAnnotationPresent(SerializeField.class))