Transforming Complex Data Using Structs
You can dynamically transform complex data structures, such as XML, JSON, or Uniface component structures, into another format using in-memory data structures called Structs.
The ability to transform complex data from one format to another is a common requirement. For example, web services often use and produce XML that does not directly correspond to Uniface data structures such as entities and lists. For Uniface applications to consume or produce complex data, it needs to be converted to or from Uniface entities, lists, or XML streams.
By creating and manipulating Structs, you can:
- Convert XML or JSON data received from web services into data structures that Uniface can handle, such as a component data structure
- Convert the data in a Uniface component into a nested XML or JSON data structure
- Merge data from different components for use in another component
- Extract desired information from any type of complex data structure
Data Type struct
The struct data type is used for complex data that is typically represented as a tree, with nodes (sub-trees or branches) and leaves (data values). It is intended for data manipulation rather than storing data, so it is available only for variables (local, component, global, and general) and parameters, but not for fields.
Assigning a value to a variable or parameter of type struct, implicitly creates a tree-like data structure in memory known as a Struct. It has a single top node, and one or more sub-nodes called members. Each member can hold a scalar data value (such as a string, date, or numeric) or it can be another Struct.
Note: The term Struct (capitalized) is used generically for data objects of type struct, whether they are top nodes or members. The term struct (lowercase, monospace font) is used for the ProcScript data type.
This distinction is important because a struct variable or parameter does not actually hold data. Instead, it holds an ordered collection of references to one or more Structs. This means that several variables can refer to the same Struct, and a change made through one of these variables is reflected in the other variables that refer to the same Struct. In this respect the struct type is like the handle type. It is very different from scalar value types such as string—if you have two string variables with the same values, changing the value of one does not affect the value of the other.
Uniface provides specialized operators, functions, and ProcScript instructions that enable you to manipulate data in a Struct. The following topics provide more information on Structs and how you can manipulate them in ProcScript to transform complex data into other formats.
Struct Helper Libraries
Uniface provides a Global ProcScript library of helper functions that make using Structs easier, and a snippet library with examples for calling these functions.
The USTRUCT Snippet library is available in the set of IDE templates. Select the USTRUCT Snippet library (libsnp:USTRUCT) in your Resource Browser to insert and use the snippets.
The USTRUCT Global ProcScript library is available in the Uniface runtime. To inspect or modify this library, import the source code (libprc.ustruct.xml) from the uniface/misc folder. Development documentation is included in the code as comments. After making any changes to this library, compile it into usys.uar to call it from your application.
The USTRUCT Global ProcScript library also includes a unit test (UTEST). This is available to test any changes or extensions you make to the library. Call this test as follows:
call ustruct::utest()
Note: The USTRUCT helper libraries are exception-enabled by default.
Structs and Memory
Structs are typically used to perform complex transformations from one format to another. This can be memory intensive because:
- A Struct exists in memory as long as there is a struct variable or parameter that points to it.
- To handle the transformation, annotations are added to Struct nodes.
As a consequence, a Struct leaf with a small amount of data (a few bytes) has a relatively large overhead, and a large Struct with small leaves needs much more memory than the data in individual nodes would need. If the data in an individual Struct leaf is large (for example, several hundred bytes), the overhead is smaller.
This is something to be considered while writing transformation logic using Structs. To control memory usage, we recommend that you handle large conversions in batches. If the Struct is very large or complex, or Struct nodes contain many annotations, Uniface may run out of memory and exit without warning. This depends on the platform and the amount of memory available.