Читайте также: Часто задаваемые вопросы На данный момент библиотека доступна только для языка С++ Для работы вам потребуется "BasicParser32.lib" и заголовочные файлы, все это доступно для загрузки: USDS Basic parserВ архив также добавлен исходный код данного примера. В примере показано, как сформировать (Клиент) и декодировать (Сервер) бинарные документы USDS.
Клиентская часть
Шаг 1: создаем и инициализируем парсерДля работы парсера необходимо обязательно задать схему данных - Словарь. Один из способов сделать это - передать парсеру текстовое описание словаря:
// Create objects for work BasicParser* clientParser = new BasicParser(); BinaryOutput* usds_binary_doc = new BinaryOutput();
// Init parser by the dictionary const char* text_dictionary = "USDS DICTIONARY ID=1000000 v.1.0 \ { \ 1: STRUCT internalObject \ { \ 1: UNSIGNED VARINT varintField; \ 2: DOUBLE doubleField; \ 3: STRING<UTF-8> stringField; \ 4: BOOLEAN booleanField; \ } RESTRICT {notRoot;} \ \ 2: STRUCT rootObject \ { \ 1: INT intField; \ 2: LONG longField; \ 3: ARRAY<internalObject> arrayField;\ } \ }";
clientParser->addDictionaryFromText(text_dictionary, strlen(text_dictionary), USDS_UTF8); Полные правила формирования словаря приведены здесь: Спецификация текстового словаря Парсер может принять несколько словарей с различными ID и версиями, для этого либо передайте ему все словари в одном текстовом блоке, либо вызовите метод "addDictionaryFromText" несколько раз. Далее вы можете быстро переключать парсер между словарями при формировании бинарных документов USDS. При декодировании бинарных документов подходящий словарь будет выбран автоматически. Шаг 2: формируем DOM-объект
Построим DOM-объект, который в дальнейшем будет преобразован в бинарный документ USDS. Добавляем в корневой раздел DOM-объекта тэг "rootObject" и инициализируем его поля: // Add new structure to the DOM object UsdsStruct* tag = clientParser->addStructTag("rootObject"); // Init simple fields tag->setFieldValue("intField", 1234); tag->setFieldValue("longField", 5000000000); Одно из полей структуры - массив тегов "arrayField" и сейчас его размер нулевой, выполним его инициализацию:
// Init array field, get link from the struct UsdsArray* array_field = tag->getArrayField("arrayField"); for (int i = 0; i < 2; i++) { // Add new element to the array, get link to this element UsdsStruct* struct_element = (UsdsStruct*)(array_field->addTagElement()); // Init element struct_element->setFieldValue("varintField", i); struct_element->setFieldValue("doubleField", i / 2.0); struct_element->setFieldValue("stringField", "String value"); struct_element->setFieldValue("booleanField", i == 0); }
Инициализация "массива структур" выполняется в два шага: - добавляем новый пустой элемент к массиву методом "addTagElement";
- выполняем инициализацию полей добавленной структуры.
В примерах выше мы инициализировали поля структур по их текстовым именам, при
этом парсер каждый раз осуществляет поиск имен в словаре, что занимает
процессорное время. В ряде случаев гораздо эффективнее предварительно
найти числовые идентификаторы тегов и полей (ID, совпадают с
объявленными в Словаре), и затем многократно выполнять обращения к полям
по их ID. Добавим в DOM-объект еще один тег "rootObject", но в этот раз воспользуемся ID: // Get tag's and fields' IDs from the dictionary int internalObject_id = clientParser->getTagID("internalObject"); int internalObject_varintField_id = clientParser->getFieldID(internalObject_id, "varintField"); int internalObject_doubleField_id = clientParser->getFieldID(internalObject_id, "doubleField"); int internalObject_stringField_id = clientParser->getFieldID(internalObject_id, "stringField"); int internalObject_booleanField_id = clientParser->getFieldID(internalObject_id, "booleanField");
int rootObject_id = clientParser->getTagID("rootObject"); int rootObject_intField_id = clientParser->getFieldID(rootObject_id, "intField"); int rootObject_longField_id = clientParser->getFieldID(rootObject_id, "longField"); int rootObject_arrayField_id = clientParser->getFieldID(rootObject_id, "arrayField");
// Now use these IDs // Add second structure to the DOM object tag = clientParser->addStructTag(rootObject_id); // Init simple fields tag->setFieldValue(rootObject_intField_id, 4321); tag->setFieldValue(rootObject_longField_id, 6000000000); // Init array field, get link from the struct array_field = tag->getArrayField(rootObject_arrayField_id); for (int i = 0; i < 2; i++) { // Add new element to the array, get link to this element UsdsStruct* struct_element = (UsdsStruct*)(array_field->addTagElement()); // Init element struct_element->setFieldValue(internalObject_varintField_id, i * 2); struct_element->setFieldValue(internalObject_doubleField_id, i / 3.0); struct_element->setFieldValue(internalObject_stringField_id, "Second root object"); struct_element->setFieldValue(internalObject_booleanField_id, i != 0); } Если идентификаторы полей и тегов вам известны заранее (совпадают с указанными в словаре), то шаг их поиска можно пропустить.
Шаг 3: формируем JSON
Теперь в нашем DOM-объекте два тега, убедимся в этом: сформируем JSON и выведем его на экран: // Let's look what we have in DOM-object std::string json; clientParser->getJSON(USDS_UTF8, &json); std::cout << "JSON:\n" << json << "\n"; std::cout << "JSON string size: " << json.size() << " symbols\n\n"; На экране получаем следующее: JSON: { "rootObject": { "intField": 1234, "longField": 5000000000, "arrayField": [ { "varintField": 0, "doubleField": 0.000000, "stringField": "String value", "booleanField": true }, { "varintField": 1, "doubleField": 0.500000, "stringField": "String value", "booleanField": false } ] }, "rootObject": { "intField": 4321, "longField": 6000000000, "arrayField": [ { "varintField": 0, "doubleField": 0.000000, "stringField": "Second root object", "booleanField": false }, { "varintField": 2, "doubleField": 0.333333, "stringField": "Second root object", "booleanField": true } ] } } JSON string size: 694 symbols Шаг 4: формирование бинарного документа USDS
Мы убедились в правильности DOM-объекта, теперь сформируем бинарный документ USDS. В него войдут Заголовок, Словарь и Тело документа: // Create USDS binary document with Head, Dictionary and Body inside clientParser->encode(usds_binary_doc, true, true, true); std::cout << "USDS binary document size: " << usds_binary_doc->getSize() << " bytes\n\n"; На экран будет выведен размер бинарного документа: "USDS binary document size: 286 bytes". Примерно половину документа занимает Словарь (140 байт). Если в DOM-объект добавить не 2, а 1000 тегов, то соотношение "словарь - тело" будет более приятным. Если Клиенту и Серверу словарь известен заранее, то формируйте бинарные документы без словаря:
clientParser->encode(usds_binary_doc, true, false, true); Шаг 5: очистка DOM-объекта
Выполним очистку DOM-объекта, но не будем удалять словарь, чтобы заново не инициализировать его в будущем: clientParser->clearBody(); Обратите внимание, что созданные объекты классов для DOM внутри парсера не были уничтожены, они будут переиспользованы при следующем обращении к парсеру (Object Pool). Это существенно ускоряет построение нового DOM-объекта. Полностью все объекты удаляются при вызове деструктора парсера. Сформированный бинарный документ все еще доступен внутри объекта "usds_binary_doc", вы можете получить ссылку на массив данных: const unsigned char* binary_data = usds_binary_doc->getBinary(); size_t binary_size = usds_binary_doc->getSize(); Передайте массив Серверу любым удобным для вас способом. Серверная часть
Шаг 1: создание парсера
Создадим парсер на стороне сервера: // Create object for work BasicParser* serverParser = new BasicParser(); Инициализация словаря в данном случае не является обязательной: словарь будет автоматически получен из первого поступившего бинарного документа USDS (при условии, что вы его туда включили на стороне Клиента). Парсер хранит и использует все уникальные словари, полученные из бинарных документов вплоть до вызова метода "Clear". Шаг 2: чтение бинарного документа
Выполним чтение бинарного документа: serverParser->decode(binary_data, binary_size); На этом этапе уже создан DOM-объект, осталось только его прочитать. Проверим, что сейчас в DOM, преобразовав его в JSON: // DOM object created from the binary // Let's look what we have in it json.clear(); serverParser->getJSON(USDS_UTF8, &json); std::cout << "JSON:\n" << json << "\n"; std::cout << "JSON string size: " << json.size() << " symbols\n\n"; На экране получаем следующее:
JSON: { "rootObject": { "intField": 1234, "longField": 5000000000, "arrayField": [ { "varintField": 0, "doubleField": 0.000000, "stringField": "String value", "booleanField": true }, { "varintField": 1, "doubleField": 0.500000, "stringField": "String value", "booleanField": false } ] }, "rootObject": { "intField": 4321, "longField": 6000000000, "arrayField": [ { "varintField": 0, "doubleField": 0.000000, "stringField": "Second root object", "booleanField": false }, { "varintField": 2, "doubleField": 0.333333, "stringField": "Second root object", "booleanField": true } ] } } JSON string size: 694 symbols Документ корректен, можно начинать чтение DOM-объекта. Шаг 3: чтение тегов и полей
Предположим, что мы не знаем структуру объекта, который пришел на сервер. Определим тип и имя первого тега в документе: Text Box// Extract first root object UsdsBaseType* someTag = serverParser->getFirstTag(); std::cout << "The type of the first Tag: '" << someTag->getTypeName() << "'\n"; std::cout << "The name of the first Tag: '" << someTag->getName() << "'\nFields:\n"; if (someTag->getType() != USDS_STRUCT) return 1; else tag = (UsdsStruct*)someTag;
На экране получаем: The type of the first Tag: 'STRUCT' The name of the first Tag: 'rootObject' Ответ нас устраивает, можно попробовать прочитать значения полей: // buffers for the field values int int_value = 0; long long long_value = 0; long long varint_value = 0; double double_value = 0.0; const char* string_value = 0; bool boolean_value = false;
// Extract the fields by the names tag->getFieldValue("intField", &int_value); std::cout << "\tintField = " << int_value << "\n"; tag->getFieldValue("longField", &long_value); std::cout << "\tlongField = " << long_value << "\n";
Если парсер не сможет привести тип тега к типу языка программирования, будет брошено исключение, т.е. не требуется проверять коды ошибок после выполнения каждой функции. Выполним разбор массива: // Extract array field array_field = tag->getArrayField("arrayField"); int array_size = array_field->getElementNumber(); for (int i = 0; i < array_size; i++) { // Get the element from the array UsdsStruct* struct_element = (UsdsStruct*)(array_field->getTagElement(i)); struct_element->getFieldValue("varintField", &varint_value); std::cout << "\tarrayField[" << i << "].varintField = " << varint_value << "\n"; struct_element->getFieldValue("doubleField", &double_value); std::cout << "\tarrayField[" << i << "].doubleField = " << double_value << "\n"; struct_element->getFieldValue("stringField", &string_value); std::cout << "\tarrayField[" << i << "].stringField = " << string_value << "\n"; struct_element->getFieldValue("booleanField", &boolean_value); std::cout << "\tarrayField[" << i << "].booleanField = " << boolean_value << "\n"; } Выполним чтение следующего тега с использованием ID полей и тегов, вместо их текстовых имен:
// Get second tag from tag = (UsdsStruct*)(tag->getNext()); std::cout << "\nThe name of the second Tag: '" << tag->getName() << "'\nFields:\n";
// Let's optimise performance // Get tag's and fields' IDs from the dictionary internalObject_id = serverParser->getTagID("internalObject"); internalObject_varintField_id = serverParser->getFieldID(internalObject_id, "varintField"); internalObject_doubleField_id = serverParser->getFieldID(internalObject_id, "doubleField"); internalObject_stringField_id = serverParser->getFieldID(internalObject_id, "stringField"); internalObject_booleanField_id = serverParser->getFieldID(internalObject_id, "booleanField");
rootObject_id = serverParser->getTagID("rootObject"); rootObject_intField_id = serverParser->getFieldID(rootObject_id, "intField"); rootObject_longField_id = serverParser->getFieldID(rootObject_id, "longField"); rootObject_arrayField_id = serverParser->getFieldID(rootObject_id, "arrayField");
// Extract the fields by the IDs tag->getFieldValue(rootObject_intField_id, &int_value); std::cout << "\tintField = " << int_value << "\n"; tag->getFieldValue(rootObject_longField_id, &long_value); std::cout << "\tlongField = " << long_value << "\n"; // Extract array field array_field = tag->getArrayField(rootObject_arrayField_id); array_size = array_field->getElementNumber(); for (int i = 0; i < array_size; i++) { // Get the element from the array UsdsStruct* struct_element = (UsdsStruct*)(array_field->getTagElement(i)); // Init element struct_element->getFieldValue(internalObject_varintField_id, &varint_value); std::cout << "\tarrayField[" << i << "].varintField = " << varint_value << "\n"; struct_element->getFieldValue(internalObject_doubleField_id, &double_value); std::cout << "\tarrayField[" << i << "].doubleField = " << double_value << "\n"; struct_element->getFieldValue(internalObject_stringField_id, &string_value); std::cout << "\tarrayField[" << i << "].stringField = " << string_value << "\n"; struct_element->getFieldValue(internalObject_booleanField_id, &boolean_value); std::cout << "\tarrayField[" << i << "].booleanField = " << boolean_value << "\n"; } Шаг 4: очистка парсера
Очищаем DOM-объект парсера на сервере, не затрагивая Словарь. При обработке следующего документа с таким же ID парсер потратит меньше времени на десериализацию, т.к. не потребуется создавать в памяти объекты для Словаря. Text BoxclientParser->clearBody();
Шаг 5: очистка памяти
Для уничтожения всех использованных объектов на клиенте и на сервере достаточно выполнить: delete clientParser; delete serverParser; delete usds_binary_doc; Object Pool внутри парсера будет также уничтожен. 2015.11.05, Андрей Абрамов
CommentsНе удалось найти URL спецификации гаджета |