Read also: FAQ At the moment the library supports only one program language: C++ To work with this software, you need "BasicParser32.lib" and headers. You can download these files here: USDS Basic parserIn the archive you will also find the source code for this example. In the example you will learn, how to create the binary document (client side) and how to parse it (server side).
Client Side
Step 1: Create and Initialize Parser
Parser works with "Scheme" only; for this reason, you have to define the dictionary. One method to do this is to input a text description of the dictionary into the parser:
// 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); The full description of the text dictionary can be found here: Specification of the Text Dictionary
The parser can contain several dictionaries - add multiple dictionaries to one text block or execute the method "addDictionaryFromText" several times. You can switch dictionaries rapidly for creating binary documents. When the parser decodes binary documents it selects the dictionary automatically.
Step 2: Create DOM-object
Let's create a DOM-object which will be encoded in the binary document USDS. Add the first root element "rootObject" and initialize it: // Add new structure to the DOM object UsdsStruct* tag = clientParser->addStructTag("rootObject"); // Init simple fields tag->setFieldValue("intField", 1234); tag->setFieldValue("longField", 5000000000); One of the fields is an array of the tag; its size is zero at the moment. Let's initialize it:
// 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); }
Initializing the array of the structures is done by: - Add new empty element to the array using the method "addTagElement";
- Initialize the added structure.
In the example above, we used the text names of the fields for initialization. This method takes a lot of processor time, because the parser has to find the fields in the dictionary. The better method is to find integer identifiers for all tags and fields and to use them for initialization. Let's do it: // 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); } If you know all integer identifiers beforehand (they are indicated in the text dictionary), you can use them at once.
Step 3: Create JSON
We have two tags in the DOM-object. Let's look at them in JSON format:
// 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"; On the screen we will see the following: 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 Step 4: Create Binary Document USDS
We have seen that we really have two structures in the DOM-object. Let's create a binary document USDS. It will contain the head, the dictionary and the body:
// 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"; On the screen we see the size of the binary document - "USDS binary document
size: 286 bytes". The dictionary is about half of the document (140 bytes). If you add to the DOM-object not 2, but 1000 tags, the ratio "dictionary - body" will be better. If the client and the server know the dictionary beforehand, then the client can create a binary document without a dictionary: clientParser->encode(usds_binary_doc, true, false, true); Step 5: Cleaning DOM-object
Let's clear away the DOM-object, but we will not delete the dictionary in the parser, so as not to reinitialize: clientParser->clearBody(); The objects of the classes inside the parser were not destroyed--they will be used again in the next DOM-object (Object Pool). It makes the process faster. All objects will be destroyed when the parser destructor is executed.
The binary document is still available inside the object "usds_binary_doc"; you can get the link to the data array: const unsigned char* binary_data = usds_binary_doc->getBinary(); size_t binary_size = usds_binary_doc->getSize(); Send the array to the server as you like. Server Side
Step 1: Create Parser
Let's create the parser on the server side: // Create object for work BasicParser* serverParser = new BasicParser(); The dictionary initialization isn't necessary - the parser will get the dictionary from the first USDS binary document (if you added it to the document). The parser stores all unique dictionaries before the method "Clear" is executed.
Step 2: Read the Binary Document
Let's read the binary document: serverParser->decode(binary_data, binary_size); Now the DOM-object is ready. You can look at it in JSON format:
// 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"; On the screen we have the following:
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 As we can see, the document is correct. We can now read the DOM-object.
Step 3: Reading Tags and Fields
Let us assume that we do not know the structure of the binary document. Let's check the type and the name of the first element: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;
On the screen we have the following: The type of the first Tag: 'STRUCT' The name of the first Tag: 'rootObject' This is a good response for our program. We can try to read the fields: // 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";
If the parser cannot convert value from the binary document to your variables, an exception will be thrown (i.e. it is not necessary to check for errors after every step).
Let's read the array: // 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"; } We can read all tags and fields using the integer identifiers (it will be faster):
// 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"; } Step 4: Clear Parser
If you clear only the parser's body, the dictionary can be used for decoding the next binary document (if it has the same dictionary ID). This method works faster. Text BoxclientParser->clearBody();
Step 5: Destroy All Objects
Execute the following methods for memory cleaning: delete clientParser; delete serverParser; delete usds_binary_doc; The Object Pool inside the parser will be destroyed too.
2015.11.06, Andrey Abramov
 CommentsThe gadget spec URL could not be found |