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, используя необязательные поля).
И наконец пример в сериализованном виде:
Быстродействие
Серьезных исследований не проводилось, но в сравнении с 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. Несмотря на версию альфа — уже можно использовать для простых случаев.
Комментарии (4)
bearded_guy
18.07.2017 12:15Делал подобную шутку, до для C# и для PER сериализации. Очень раздражало, что в спецификации о том как и где лежат биты нет ни одной картинки, а все написано словами.
lastrix
18.07.2017 12:19Рекомендую заглянуть сюда: http://asn1-playground.oss.com/
Так и проверял, правильно ли я понял доки. Все интеграционные тесты "а" в бинарном виде получены как раз из этого сервиса.
evnp
Пару лет назад было прям очень нужно и выбрали http://www.unigone.com/en/asn1-solutions/asn1compiler/ как самое в тестах адекватное решение (ничего подходящего свободного не нашлось), но потом проект все равно провалился :)
В любом случае спасибо, может еще кому это пригодится. Нам правда актуальнее были не аннотации, а генерация бинов по существующей схеме.
lastrix
Основной упор был на поддержку спецификаций (X.680-X.683) для чтения и представления схем в модель. Иначе что-либо делать далее не имеет смысла.
Аннотации в данный момент — рудимент, который нужно будет переписать с нуля. На это дело потрачено часов 10. Для сравнения, суммарно (включая предыдущую попытку, которая так же на гитхабе у меня валяется) на проект ушло более 300 часов.
Что же до генерации бинов — это не такая сложная задача. И ставить себе такую задачу не было желания, т.к. проект мой ничем не отличался бы от уже существующих опенсорсных аналогов.
Проект писался и пишется исключительно по фану и ради удовольствия.