Create a new adapter

If you need more control on the serialization of an object than what the default adapter options can offer, you can register custom adapter.


To register an adapter you need to give the instance of the adapter to use, you can also give a type alias to rename the object type the same way.

odin.register(adapterInstance1);
odin.register("foo", adapterInstance2);

Adapter can be one of OdinAdapter implementation listed below.

Inline Adapter

An inline adapter converts an object to another object, most of the embedded adapters use this to format an object to a String ( dates, uuid, class, ... ) but you can return what you want.


If the returned value is a sub class of the type defined in the adapter declaration, the type alias is added.

public class FooAdapter implements OdinInlineAdapter<Foo, String> {
    
    @Override
    public Foo read(ObjectType<? extends Foo> type, String o) throws IOException {
        return Foo.fromString(o);
    }
    
    @Override
    public String write(ObjectType<? extends Foo> type, Foo o) throws IOException {
        return Foo.toString(o);
    }
}

Object Adapter

This is the standard object adapter. When you define an object adapter, you also receive your object instance in the read method. The instance is built using default constructor or an unsafe empty constructor.


You can read only a part of the object content. If the object contains more entries than you have read, these entries will be skipped.

public class FooAdapter implements OdinObjectAdapter<Foo> {
    
    @Override
    public void read(OdinReader reader, ObjectType<? extends Foo> type, Foo o) throws IOException {
        reader.readField("value").listenInt((Integer i) -> o.setValue(i);
    }
    
    @Override
    public void write(OdinWriter writer, ObjectType<? extends Foo> type, Foo o) throws IOException {
        writer.writeField("value").withInt(o.getValue());
    }
}

Don't forget that the object you read can be of a sub type of your adapter declaring type.

Immutable Adapter

If you need to pass some content to the object constructor, you can use an immutable adapter. This adapter is the same as an object adapter, the only difference is that you build and return the object instead of receiving it.


public class FooAdapter implements OdinImmutableAdapter<Foo> {
    
    @Override
    public Foo read(OdinReader reader, ObjectType<? extends Foo> type) throws IOException {
        while (reader.hasNext()) {
            String key = reader.readNextKey();
            if ("value".equals(key)) {
                return new Foo(reader.readInt());
            }
        }
    }
    
    @Override
    public void write(OdinWriter writer, ObjectType<? extends Foo> type, Foo o) throws IOException {
        writer.writeField("value").withInt(o.getValue());
    }
}

Don't forget that the object you read can be of a sub type of your adapter declaring type.

Runtime Adapter

If an object implements OdinObjectAdapter, the object will use this adapter instead of the registered adapter.

So when the object is read or written, the methods of the adapter are called using the current object.

public class Foo implements OdinObjectAdapter<Foo> {
    
    int value;
    
    @Override
    public void read(OdinReader reader, ObjectType<? extends Foo> type, Foo o) throws IOException {
        reader.readField("value").listenInt((Integer i) -> this.value = i);
    }
    
    @Override
    public void write(OdinWriter writer, ObjectType<? extends Foo> type, Foo o) throws IOException {
        writer.writeField("value").withInt(value);
    }
}

Runtime adapter cannot be used for OdinInlineAdapter and OdinImmutableAdapter because the instance is not known before the read method is called.