Автоматическая сериализация Java-объектов в ASN.1

в 10:54, , рубрики: asn.1, java

ASN.1 — стандарт описания протоколов передачи данных. Вот уже 8 лет не дает покоя тот факт, что существуют либо слабые попытки его использовать, практически не прибегая к его полному функционалу, либо же это монстры за большие деньги, используемые отнюдь не рядовым Васей.
Причем и те и другие наследуют одни и те же черты — неудобство, перегруженность дополнительными действиями и генерацией совсем бесполезных заглушек.
На хабре появлялось уже достаточно много статей с описанием всех прелестей ASN.1, рекомендую почитать их.

Вашему вниманию предлагаю альфа-версию библиотеки, позволяющей в стиле hibernate или jackson-databind сохранить ваши данные в бинарном виде. Пока поддерживаются только нотации BER(DER).

Примеры использования

ASN.1 S уже сейчас позволяет сериализовать классы с примитивами (и их упакованной версией):

@Sequence( name = "Message", tagNumber = 2 )
public class Message
{
    @Property( optional = true )
    private Integer id;
    @Property( typeName = "GeneralizedTime" )
    private Instant stamp;
    @Property
    private String text;
    @Constructor
    public Message( @ConstructorParam( value = "manager", global = true ) ObjectManager manager, @ConstructorParam( "id" ) Integer id, @ConstructorParam( "stamp" ) Instant stamp, @ConstructorParam( "userId" ) int userId, @ConstructorParam( "text" ) String text )
    {
        this.id = id;
        this.stamp = stamp;
        user = manager.getUser( userId );
        this.text = text;
    }
       //...
    private User user;
    @Property
    public int getUserId()
    {
        return user.getId();
    }

Разумеется задавать поля в конструкторе не обязательно. Достаточно сделать все поля доступными для записи через сеттеры и не забыть про конструктор по умолчанию (jackson вас тоже по рукам бить будет):

@Sequence( name = "User" )
public class User
{
    @Property
    private int id;
    @Property
    private String name;
    @Property
    private String accessKey;

    public User(){  }
    public User( int id, String name, String accessKey ) { ... }

    public int getId(){return id;}
    public void setId( int id ){this.id = id;}

    public String getName(){return name;}
    public void setName( String name ){this.name = name;}

    public String getAccessKey() { return accessKey; }
    public void setAccessKey( String accessKey ) { this.accessKey = accessKey; }

В данный момент наличие аннотации обязательно, но ничто не мешает сделать вариант полностью аналогичный jackson — данные можно сериализовать как есть, без всяких метаданных, написанных вами, основываясь на reflection. Сейчас этого нет в целях отладки — жесткие системы всегда проще отладить.

Так же поддерживаются массивы (тип указывается, что бы библиотека не генерировала свое — T-Java-Bind-org-asn1s-databind-tests-persons-Person-Array):

    @Property( optional = true, typeName = "Person-Array" )
    private Person[] family;

И списки, если класс элемента один:

    @Property( typeName = "NoteList" )
    private List<Note> notes;

В будущем можно будет задавать набор классов, которые могут появится в коллекции (в духе
@ItemClass({Note.class, Book.class, Paper.class}) ). Все необходимое в библиотеке есть, а ASN.1 поддерживает несколько механизмов реализации (CHOICE, INSTANCE OF, можно даже поиграться с SEQUENCE и SET, используя необязательные поля).

И наконец пример в сериализованном виде:
image

Быстродействие

Серьезных исследований не проводилось, но в сравнении с jackson ASN.1 S работает на порядок хуже.

PersonsTest.testPersonsJsonRead: [measured 100000 out of 110000 rounds, threads: 1 (sequential)]
 ... time.total: 0.33, time.warmup: 0.10, time.bench: 0.22
PersonsTest.testPersonsRead: [measured 100000 out of 110000 rounds, threads: 1 (sequential)]
 ... time.total: 1.05, time.warmup: 0.26, time.bench: 0.79
PersonsTest.testPersonsJsonWrite: [measured 100000 out of 110000 rounds, threads: 1 (sequential)]
 ... time.total: 0.21, time.warmup: 0.05, time.bench: 0.16
PersonsTest.testPersonsWrite: [measured 100000 out of 110000 rounds, threads: 1 (sequential)]
 ... time.total: 1.17, time.warmup: 0.22, time.bench: 0.95

Использование NIO позволит повысить быстродействие, в данный момент буферизация делается через стек из ByteArrayOutputStream — самый простой способ, приводящий к огромному числу операций копирования и выделения памяти.

Заключение

Представлена библиотека с открытым исходными кодом под лицензией MIT. Несмотря на версию альфа — уже можно использовать для простых случаев.

Автор: lastrix

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js