# RPG API Express 3.3 Documentation


---

# RPG-XML Suite 3.3x System Requirements

> Lists supported IBM i OS versions (7.3-7.6), required features (Apache, QShell), and installation prerequisites for RPG API Express 3.3.x.

## Installed Feature Requirements 
- RPG API Express requires Apache which is installed on 99% of all machines we come in contact with. Note that Apache only needs to be started if you are offering web services from your IBM i. If you are only acting as the client role and making a request to another server *from* the IBM i, then you don't need to start the Apache server.
- We use QShell during the install so that is also required and is already installed on most machines. It is **5722SS1 opt 30**.

## Operating System Level Support 
RPG API Express 3.3x can be used on the following operating system versions:

- IBM i OS 7.6
- IBM i OS 7.5
- IBM i OS 7.4
- IBM i OS 7.3
- IBM i OS 7.2
- IBM i OS 7.1
- IBM i OS 6.1

---

# Installing RPG-XML Suite 3.3x

> Step-by-step installation guide covering file transfer, library restoration, HTTP server creation, license activation, and verification.

RPG-XML Suite is available as a fully-featured 30 day free trial. To request a trial and a free customized proof of concept program, please [contact us](https://www.rpg-xml.com/contact-us/).

1. Unzip the downloaded file to `C:\temp` (or the directory of your choice).

2. Create the \*SAVF to upload to on your IBM i:

    `CRTSAVF FILE(QGPL/RXS) AUT(*ALL)`

3. FTP the file RXS.SAVF from your PC to the IBM i. Open a DOS prompt (Start -> Run -> enter 'cmd' and hit enter). Type the following into the DOS prompt, replacing the IP address with that of your IBM i. When prompted, enter your IBM i username and password.

    1. `ftp 123.456.789.123`
    2. `binary`
    3. `lcd c:\temp`
    4. `quote site namefmt 0`
    5. `cd QGPL`
    6. `put rxs.savf rxs.savf`
    7. `quit`

4. Issue the following IBM i commands. The value 'RXS' is used to denote where the base install of RPG-XML Suite should reside. Note that 8181 is the default port your RXS runs under in Apache. Change it to meet your needs. The default of 8181 should be fine 99% of the time.

    1. `RSTLIB SAVLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS) RSTLIB(RXS)`
    2. `RST DEV('/QSYS.LIB/RXS.LIB/IFS.FILE') OBJ(('/krengeltech')) CRTPRNDIR(*YES) PRNDIROWN(QSYS) ALWOBJDIF(*ALL)`
    3. `ADDLIBLE RXS`
    4. `CRTRXSSVR HTTPSVR(*RXSLIB) RXSLIB(RXS) PORT(8181)`

5. Registration - When you downloaded RXS, you should have been prompted to enter in a valid email address. An email should have been sent to that address specifying an APYLIC command similar to the one below. Issue the emailed command using copy-and-paste to avoid input errors.

    `APYLIC LICKEY(<<insert key that was emailed to you>>) PRODUCT(RXS)`

6. Start the HTTP server using the below command:

    `HTTP HTTPSVR(RXS) OPTION(*START)`

7. Please check the value of the QSECURITY system value:

    `DSPSYSVAL SYSVAL(QSECURITY)`

    If the value is 30 or lower, then you can proceed to the next step.

    If the value is 40 or above, please ensure that the value of QALWUSRDMN is \*ALL, or optionally add RXS to the list of libraries:

    `WRKSYSVAL SYSVAL(QALWUSRDMN)`

8. Open your internet browser and enter the following while substituting your IBMi IP address:

    `http://yourIBMiIP:8181/debug`

    You should see a page used for general debugging of the RPG-XML Suite Router. If you see this page, then the RXS Router is installed properly.

9. Open your internet browser and enter the following while substituting your IBMi IP address:

    `http://yourIBMiIP:8181/MYRXS/rxs1`

    You should get the following result:

    `<output myAttr="static value">I love home improvement plumbing. Especially when it leaks after you turn on the water!</output>`

---

# Upgrading RPG-XML Suite 3.3x

> Upgrade procedures for single and multiple library installations, including RSTOBJ commands, library list management, and Apache instance handling.

The upgrade process for RPG-XML Suite is dependent on how many libraries contain RPG-XML Suite objects. You can determine if RPG-XML Suite is installed in multiple libraries with this command:

`WRKOBJ OBJ(*ALL/RXS*) OBJTYPE(*SRVPGM)`

If the WRKOBJ command lists only library RXS, you should use the upgrade instructions for **Single Library Installation** below.

If the WRKOBJ command lists libraries other than RXS, please use the upgrade instructions for **Multiple Library Installation** below.

Additionally, if you have a library named KTLIC on your IBM i, please follow the instructions for **KTLIC Library Deprecation** below before beginning any upgrade.

### Single Library Installation

**Note:**
- `@@@` is the version number of the source/current version level (example: 220)
- `###` is the version number of the target/new version level (example: 335)

For clarity, you may wish to copy these instructions to a text editor and then perform a scan and replace for the above values before beginning the upgrade.

1. Unzip the downloaded upgrade file to `C:\temp` (or the directory of your choice).

2. Issue the IBM i command:

    `CRTSAVF FILE(QGPL/RXS###) AUT(*ALL)`

3. FTP the file rxs.savf from your PC to the IBM i in BINARY mode into the save file that was just created:

    1. Open a DOS prompt (Start -> Run -> type 'cmd' and hit Enter)
    2. Type the following into the DOS prompt, replacing the IP address with that of your IBM i: `ftp 127.0.0.1`
    3. When prompted, enter your user profile and password
    4. Execute the following commands in the DOS prompt to transfer the file:
        1. `binary`
        2. `lcd C:\temp`

            **Note:** Replace `C:\temp` with the location of the rxs.savf on your local computer.

        3. `quote site namefmt 0`
        4. `cd QGPL`
        5. `put rxs.savf rxs###.savf`
        6. `quit`

4. End all RPG-XML Suite Apache instances. Note that this will mean your web services are **NOT** accessible! Execute the following IBM i command to end the Apache instances:

    `ENDTCPSVR SERVER(*HTTP) HTTPSVR(RXS)`

    **Note:** Be sure to execute this command for any custom instances that also use RPG-XML Suite.

5. Perform a full backup of library RXS.

6. Execute the following IBM i commands in sequence to upgrade the library RXS. Please be careful to enter the commands EXACTLY as listed, except for adjusting the SAVF parameter:

    1. `RSTOBJ OBJ(*ALL) SAVLIB(RXS) MBROPT(*ALL) ALWOBJDIF(*FILELVL *OWNER) OMITOBJ((RXSUNQ) (RXSCFG) (LICP) (RXSRTRCTL) (RXSRTRLOG)) RSTLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS###)`

        **Note:** The above RSTOBJ command may report that file RXS was not restored. If this occurs, please rename the existing RXS file to RXS@@@ where @@@ is the pre-upgrade version level. Then retry the RSTOBJ command.

    2. `RSTOBJ OBJ(RXSUNQ RXSCFG LICP RXSRTRCTL RXSRTRLOG) SAVLIB(RXS) OPTION(*NEW) RSTLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS###)`

        **Note:** The above RSTOBJ command may report that no objects were restored. This is not an error condition and you should continue with the next step.

7. Execute the following IBM i command to restore updates to necessary files in the IFS (this is all one command):

    `RST DEV('/QSYS.LIB/RXS.LIB/IFS.FILE') OBJ(('/krengeltech')) CRTPRNDIR(*YES) PRNDIROWN(QSYS) ALWOBJDIF(*ALL)`

8. Execute the following IBM i commands when file RXS/RXSRTRCTL **does NOT** already exist:

    `RSTOBJ OBJ(RXSRTRCTL RXSRTRLOG) SAVLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS###) RSTLIB(RXS)`

9. Execute the following IBM i commands when files RXS/RXSRTRCTL and RXS/RXSRTRLOG do **NOT** already have a RXSRTR member:

    1. `RNMM FILE(RXS/RXSRTRCTL) MBR(RXSRTRCTL) NEWMBR(RXSRTR)`
    2. `RNMM FILE(RXS/RXSRTRLOG) MBR(RXSRTRLOG) NEWMBR(RXSRTR)`
    3. `CHGPF FILE(RXS/RXSRTRCTL) MAXMBRS(32)`
    4. `CHGPF FILE(RXS/RXSRTRLOG) MAXMBRS(32)`

10. Execute the following IBM i commands when your pre-upgrade version was previous to version 2.70:

    1. `RMVLIBLE LIB(RXS)`
    2. `ADDLIBLE LIB(RXS) POSITION(*FIRST)`
    3. `APYLIC LICKEY(<<insert key that was provided during your upgrade planning>>) PRODUCT(RXS)`

        **Note:** If you need a temporary license key, please email our support team at <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

11. Restart all RPG-XML Suite Apache instances:

    `STRTCPSVR SERVER(*HTTP) HTTPSVR(RXS)`

    **Note:** Be sure to execute this command for any custom instances that also use RPG-XML Suite.

### Multiple Library Installation

If you haven't yet, run the following command: `WRKOBJ OBJ(*ALL/RXS*) OBJTYPE(*SRVPGM)`

Using the libraries output by the WRKOBJ command, please make a list of the libraries which need to be upgraded, but do not include the library RXS on the list. There should be an Apache instance for each library and the instance name should be the same as the library. As you continue with these instructions, this list will be referred to as the "Upgrade List". These instructions will have you first perform the upgrade to your RXS library, then to each library on the Upgrade List.

These instructions will reference the library and Apache instance named MYRXS. You may or may not have a MYRXS library and Apache instance on your Upgrade List. Regardless, when upgrading each library on your Upgrade List, substitute that library's name in place of MYRXS in the following instructions.

**Note:**
- `@@@` is the version number of the source/current version level (example: 22)
- `###` is the version number of the target/new version level (example: 310)

For clarity, you may wish to copy these instructions to a text editor and then perform a scan and replace for the above values before beginning the upgrade.

1. Unzip the downloaded upgrade file to `C:\temp` (or the directory of your choice).

2. Issue the IBM i command:

    `CRTSAVF FILE(QGPL/RXS###) AUT(*ALL)`

3. FTP the file rxs.savf from your PC to the IBM i in BINARY mode into the save file that was just created:

    1. Open a DOS prompt (Start -> Run -> type 'cmd' and hit Enter)
    2. Type the following into the DOS prompt, replacing the IP address with that of your IBM i: `ftp 127.0.0.1`
    3. When prompted, enter your user profile and password
    4. Execute the following commands in the DOS prompt to transfer the file:
        1. `binary`
        2. `lcd C:\temp`

            **Note:** Replace `C:\temp` with the location of the rxs.savf on your local computer.

        3. `quote site namefmt 0`
        4. `cd QGPL`
        5. `put rxs.savf rxs###.savf`
        6. `quit`

4. End all RPG-XML Suite Apache instances. Note that this will mean your web services are **NOT** accessible! Execute the following IBM i command to end the Apache instances, while substituting the instance names from the Upgrade List for MYRXS:

    `ENDTCPSVR SERVER(*HTTP) HTTPSVR(MYRXS)`

    **Note:** Be sure to execute this command for any custom instances that also use RPG-XML Suite.

5. Perform full backups of library RXS and all libraries on the Upgrade List.

6. Execute the following IBM i commands in sequence to upgrade the library **RXS**. Please be careful to enter the commands EXACTLY as listed, except for adjusting the SAVF parameter:

    1. `RSTOBJ OBJ(*ALL) SAVLIB(RXS) MBROPT(*ALL) ALWOBJDIF(*FILELVL *OWNER) OMITOBJ((RXSUNQ) (RXSCFG) (LICP) (RXSRTRCTL) (RXSRTRLOG)) RSTLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS###)`

        **Note:** The above RSTOBJ command may report that file RXS was not restored. If this occurs, please rename the existing RXS file to RXS@@@ where @@@ is the pre-upgrade version level. Then retry the RSTOBJ command.

    2. `RSTOBJ OBJ(RXSUNQ RXSCFG LICP RXSRTRCTL RXSRTRLOG) SAVLIB(RXS) OPTION(*NEW) RSTLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS###)`

        **Note:** The above RSTOBJ command may report that no objects were restored. This is not an error condition and you should continue with the next step.

7. Execute the following IBM i command to restore updates to necessary files in the IFS (this is all one command):

    `RST DEV('/QSYS.LIB/RXS.LIB/IFS.FILE') OBJ(('/krengeltech')) CRTPRNDIR(*YES) PRNDIROWN(QSYS) ALWOBJDIF(*ALL)`

8. Execute the following IBM i commands when file RXS/RXSRTRCTL **does NOT** already exist:

    `RSTOBJ OBJ(RXSRTRCTL RXSRTRLOG) SAVLIB(RXS) DEV(*SAVF) SAVF(QGPL/RXS###) RSTLIB(RXS)`

9. Execute the following IBM i commands when files RXS/RXSRTRCTL and RXS/RXSRTRLOG **do NOT** already have a RXSRTR member:

    1. `RNMM FILE(RXS/RXSRTRCTL) MBR(RXSRTRCTL) NEWMBR(RXSRTR)`
    2. `RNMM FILE(RXS/RXSRTRLOG) MBR(RXSRTRLOG) NEWMBR(RXSRTR)`
    3. `CHGPF FILE(RXS/RXSRTRCTL) MAXMBRS(32)`
    4. `CHGPF FILE(RXS/RXSRTRLOG) MAXMBRS(32)`

10. Execute the following IBM i commands when your pre-upgrade version was previous to version 2.70:

    1. `RMVLIBLE LIB(RXS)`
    2. `ADDLIBLE LIB(RXS) POSITION(*FIRST)`
    3. `APYLIC LICKEY(<<insert key that was provided during your upgrade planning>>) PRODUCT(RXS)`

        **Note:** If you need a temporary license key, please email our support team at <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

11. Execute the following IBM i commands to upgrade **all the libraries on the Upgrade List**, while substituting the names from the Upgrade List for MYRXS:

    1. **Note:** The following string is a <u>single command</u> - you must copy the entire string. You may need use the extended command line accessed via STRPDM.

        `RSTOBJ OBJ(*ALL) SAVLIB(RXS) MBROPT(*ALL) ALWOBJDIF(*FILELVL *OWNER) OMITOBJ((RXSUNQ) (RXSCFG) (LICP) (INSTALL) (NEWENV) (HTTP*) (DSPMCH*) (CRTRXS*) (QTXT) (REGRXSBASE) (VER*) (RXSRTR* *FILE) (*ALL *PNLGRP) (RXSURI*) (DSPVER) (RXSMENU*) (INZ*) (WRK*) (CRT*)) RSTLIB(MYRXS) DEV(*SAVF) SAVF(QGPL/RXS###)`

        **Note:** The above RSTOBJ command may report that file RXS was not restored. If this occurs, please rename the existing RXS file to RXS@@@ where @@@ is the pre-upgrade version level. Then retry the RSTOBJ command.

    2. `CPYF FROMFILE(RXS/LICP) TOFILE(MYRXS/LICP) MBROPT(*REPLACE) CRTFILE(*YES)`

12. Restart all RPG-XML Suite Apache server instances by executing the following command, while substituting the names from the Upgrade List for MYRXS:

    `STRTCPSVR SERVER(*HTTP) HTTPSVR(MYRXS)`

    **Note:** Be sure to execute this command for each instance name on the Upgrade List and for any custom instances that also use RPG-XML Suite.

### KTLIC Library Deprecation

Library KTLIC was installed with version level 2.7x only and has been deprecated since version 2.80. KTLIC is no longer needed after upgrading to version 2.80 or higher. If your pre-upgrade version level is already 2.80 or higher, do not run the CPYF command below and instead note the reference to saving and deleting KTLIC.

If your pre-upgrade version is 2.7x, execute the following IBM i commands before beginning any upgrade:

`CPYF FROMFILE(KTLIC/LICP) TOFILE(RXS/LICP) MBROPT(*REPLACE) CRTFILE(*YES)`

After your upgrade to a version 2.80 or greater is complete and proven successful, please save and then delete library KTLIC.

---

# Uninstalling RPG-XML Suite

> Covers removing RPG API Express by terminating Apache instances, deleting the library, and removing IFS directories.

RPG-XML Suite licenses may be transferred to a different IBM i, or a different partition. Part of this process will involve uninstalling the product from the original machine/partition.

1. End the Apache server instance:

    `ENDTCPSVR SERVER(*HTTP) HTTPSVR(RXS)`

2. Delete the RPG-XML Suite install library:

    `DLTLIB RXS`

3. Run QShell command to recursively remove the RXS directory and everything in it:

    `QSH CMD('rm -Rf /www/RXS')`

4. Run QShell command to recursively remove the /krengeltech directory and everything in it:

    `QSH CMD('rm -Rf /krengeltech')`

5. Remove the Apache server instance from OS/400:

    `RMVM FILE(QUSRSYS/QATMHINSTC) MBR(RXS)`

6. If the below command does not return "Data area RXSBASE in QGPL not found.":

    `DSPDTAARA DTAARA(QGPL/RXSBASE)`

    Call this command to delete the RXS license object:

    `DLTOBJ OBJ(QGPL/RXSBASE) OBJTYPE(*DTAARA)`

---

# Replication Instructions

> Specifies objects to exclude from replication (QGPL/RXSBASE, LICP files) to prevent licensing errors on replicated systems.

If the following objects exist in your RPG-XML Suite installation, they should be excluded from replication. Failure to do this will cause licensing errors.

- QGPL/RXSBASE *DTAARA
- RXS/LICP *FILE
- Any other LICP *FILE objects present in RXS instance libraries (ex. MYRXS)

---

# License Transfer

> Explains how to transfer an RPG API Express license from one IBM i system or partition to another.

RPG-XML Suite licenses may be transferred to a different IBM i, or a different partition. To do this, you will need to provide us with screenshots of the following information with the RXS library in your library list:

- The currently installed version of RPG-XML Suite:
`CALL RXS/VERRXSBASE`

- Machine info for each LPAR a license is being moved from:
`DSPMCHINF`

- Machine info for each LPAR a license is being moved to:
`DSPMCHINF`

If you don't currently have access to the system you're migrating to, please let us know and we can provide a temporary license until you can provide information for us to generate a permanent license key.

Please email this information to <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

---

# RXS Changelog

> Version history for RPG API Express, detailing changes per release.

## 3.5.3 
- [New] **New feature:** Added new parameter AUTOSTART to CRTRXSSVR command to allow configuring the HTTP server's autostart property at creation time
- [New] **New feature:** Added new value *CFGAUTO for HTTP command parameter OPTION to enable new parameter AUTOSTART which allows modifying the HTTP server's autostart property after creation
- [New] **New feature:** Added new parameter STMFCCSID to RXSURI command to allow configuring the CCSID used to create response IFS stream files
- Enhanced subprocedures with more aggressive checking of input parameter data structure types to avoid unintended behavior. **If an existing program is incorrectly relying on using the wrong data structure for an RXS subprocedure, you may experience new runtime errors after upgrading.**
- Enhanced RXS_HMACDS_t with new subfield ReturnMode which accepts the constants RXS_RETURN_MODE_BYTES, RXS_RETURN_MODE_CHAR_UPPER, and RXS_RETURN_MODE_CHAR_LOWER to allow the value returned from RXS_HMAC to be returned as raw bytes, uppercase characters, or lowercase characters. ReturnMode replaces/overlays the existing subfield ReturnAsChar which only allowed raw bytes or uppercase characters to be returned by RXS_HMAC
- Enhanced RXS_ParseJsonDS_t with new subfield ParseMode which accepts the constants RXS_JSON_PARSE_DEFAULT, RXS_JSON_PARSE_NATIVE, and RXS_JSON_PARSE_NULL_EMPTY to allow the parsed values made available in a parsing handler subprocedure to be returned as either converted to character data, provided as native RPG data types, or converted to character data but with null values converted to an empty string. ParseMode replaces/overlays the existing subfield ConvertDataToString which only allowed values to be converted to character data or native RPG data types
- Enhanced RXS Router to pull Initial ASP group from a *JOBD specified for -liblst or -dftliblst to provide further compatibility with iASPs
- Enhanced RXS Router debug mode page to now display instructions to disable debug mode
- Enhanced RXS_HTTPResponseDS_t data structure with new subfield ResponseLength to provide the actual length of the response
- Enhanced RXSURI command TIMEOUT parameter with new special value *NONE to indicate no maximum timeout
- Enhanced HTTP command to allow parameter OPTION values *CONF, *LOGS, *DOCS, *CURLOG, and *DIR to more accurately retrieve the HTTP server path to support HTTP servers created on iASPs
- Enhanced BLDPRS command to now include ExtProc(*DclCase) in generated prototypes and procedure interfaces
- Enhanced BLDPRS, BLDCMP, and BLDTPL commands to throw an error when the same IFS filepath is provided for both input and output
- Corrected an issue with RXS_FormatJson where the RXS_FormatJsonDS_t data structure parameter could become incorrectly reinitialized
- Corrected an issue with RXS_ComposeJsonString where passing in an indicator value like RXS_JSON_TRUE or RXS_JSON_FALSE would lead to the JSON containing 16mb of 1s or 0s
- Corrected an issue with RXS_PutStdOut where input parameter size was being calculated multiple times
- Corrected an issue with CRTRXSSVR command PORT parameter help text displaying the wrong parameter name
- Corrected an issue with RXSURI command where output would not be displayed interactively if an input IFS file was used
- Corrected an issue with RXSURI where the help text did not accurately reflect all available options
- Corrected a typo in error message KTA120B
- Updated XML being composed as part of example programs EX3 and FX_EX3
- Removed legacy INSTALL *PGM object from installation


## 3.5.2

- Corrected an issue where an empty LICP file would result in an incorrectly thrown KTE0002 message instead of KTE0001 as intended
- Corrected an issue with RXS_ParseDomToDom where the returned RXS_ParseDomDS_t data structure would not be properly initialized if any error occured during the call to RXS_ParseDomToDom
- Corrected an issue with WRKRXSRTRE where the environment library being displayed was incorrectly truncated
- Corrected an issue with WRKRXSRTRE which could prevent modifying or adding routing entry records
- Corrected an issue with RXS_Transmit where a response with charset ISO-8859-1 could be improperly handled in certain circumstances


## 3.5.1

- Enhanced RXS Router to allow up to 250 libraries to be specified for -liblst or -dftliblst
- Enhanced WRKRXSRTRE to show the current active mode when displaying, modifying, or deleting a routing entry record
- Enhanced WRKRXSRTRE to no longer attempt validation for routing entry records for records with DEBUG as the routing ID
- Corrected an issue with library list handling in RXS Router that prevented libraries specified in -liblst or -dftliblst from being added to the library list in certain circumstances
- Corrected an issue where RXS Router's debugmode page would truncate routing entry record data for large routing entry records


## 3.5.0

- **The product name has been formally changed to RPG API Express.** This change is purely cosmetic, impacting descriptive text, help panel text, and some message text. This change has no functional impact on the usage of the product.
- **The minimum supported operating system version is now IBM i 7.3**


### Code Generation Tools 
- [New] **New feature:** BLDCMP is a new code generation command that can be passed an IFS JSON file and will generate the appropriate RPG source code which would allow for composing identical JSON using the RXS JSON composition APIs
- [New] **New feature:** Added a new optional parameter INCCRTCMD to command CRTRPGTPL which allows a user to omit the section of a generated template that contains the creation command information
- Enhanced BLDTPL to preserve namespace attributes when generating templates, rather than converting them to variable placeholders
- Enhanced CRTRPGTPL to support whitespace in front of section delimiters
- Enhanced CRTRPGTPL to better handle escaping single quotation marks when wrapping lines
- Corrected an issue where CRTRPGTPL would generate creation command information where the IFS filepath was truncated
- Corrected an issue where CRTRPGTPL would retain a lock on a source member provided as input


### HTTP Communication - RXS_Transmit and RXS_getURI() 
- [New] **New feature:** Added new subfields HeaderAuthScheme and HeaderAuthCredentials to RXS_TransmitDS_t to allow RXS_Transmit to more broadly support a variety of authentication mechanisms without needing to set custom HTTP headers. This support allows for credentials/tokens up to 8kb in length to better support complex/large JWT tokens when used as part of a Bearer authentication scheme.
- [New] **New feature:** Added new subfield HeaderAcceptEncoding to RXS_TransmitDS_t to allow RXS_Transmit to request that the remote server provide gzip encoded response data via the [Accept-Encoding HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding), and RXS_Transmit will automatically handle decoding. This should result in an overall performance increase when calling any web APIs which support gzip encoding. **Requesting that the remote server provide the response via gzip encoding is now the default behavior**
- [New] **New feature:** Added a new constant RXS_TIMEOUT_UNLIMITED to allow setting an unlimited timeout when calling RXS_Transmit
- Enhanced RXS_Transmit to allow output to be directed both to a response field as well as to a response IFS file
- Enhanced RXS_Transmit IFS log file output to properly display the response when the remote server provided the response using chunked encoding. Previously, the IFS log file would display the response split into chunks along with the chunk encoding length bytes
- Corrected a rare issue where RXS_Transmit ending in error with an IFS log file specified could attempt to generate an IFS log file on a subsequent successful RXS_Transmit call which did not request a log file
- Corrected an issue with RXS_getURI preventing zero length POST requests


### Encryption and Hashing APIs 
- [New] **New feature:** Added RXS_HMAC to perform HMAC calculation utilizing the SHA1, SHA256, SHA384, SHA512, and MD5 algorithms


### Offering Web Services/APIs 
- [New] **New feature:** Added -aspgrp flag to RXS Router to allow for swapping the ASP Group prior to any library list changes or program calls
- Enhanced the CRTRXSSVR command to support 5-digit port numbers up to 65535
- Enhanced the CRTRXSSVR command with new *HTTP and *HTTPS options for setting the port number which will cause port 80 and port 443 to be used
- Corrected an issue with the HTTP command where a validation message was not properly displayed when attempting to use parameter HTTPSVR(*ALL) with an OPTION other than *WRK, or when attempting to use HTTPSVR(*ADMIN) with an option other than *WRK, *START, *END, or *RESTART


### JSON Composition APIs 
- Corrected an issue where RXS_GetJsonString could enter a looping condition if certain hexadecimal characters were present in composed JSON data


### JSON Parsing APIs 
- Enhanced RXS_ParseJson to check input JSON for possible truncation before attempting to parse, and added specific error output to differentiate truncation as a cause of parsing failure from malformed JSON


### XML Parsing APIs 
- Corrected an issue where RXS_XPath was incorrectly escaping @ symbols that were part of a formatted XPath
- Corrected a potential memory leak present in RXS_Parse when parsing XML stored in an IFS file


### Conversion APIs 
- Enhanced RXS_Convert Base64 and Base64URL decoding to ignore whitespace characters including space, tab, CR, LF, and NL
- Corrected an issue with RXS_Convert Base64URL decoding where it would fail on certain string lengths that could not be accurately internally padded
- Corrected an issue with RXS_Convert Base64 and Base64URL decoding where line break characters could cause a failed conversion loop


### Example Programs 
- Included example programs are now provided in two forms - fully free format RPG as well as traditional/fixed format RPG
- Included example programs have been updated to reflect our team's latest recommended standards for calling and using various RXS APIs
- To provide clarity and work within the 10 character object name limit, existing example programs have been renamed - see table below:

  | ORIGINAL EXAMPLE NAME | NEW FREE FORMAT NAME | NEW FIXED FORMAT NAME |
  | --- | --- | --- |
  | CELSIUS | EX1 | FX_EX1 |
  | CELSIUSDOM | EX2 | FX_EX2 |
  | DOMRECUR | EX3 | FX_EX3 |
  | EXCOMPOSE1 | EX4 | FX_EX4 |
  | EXPARSE1 | EX5 | FX_EX5 |
  | XSDVALID | EX6 | FX_EX6 |



### Other Changes 
- New constants have been added to the RXSCB copybook for commonly used HTTP status codes
- Display file CONFIRMD has been renamed to RXSCFMD based on a user-reported conflict with a display file named CONFIRMD present in another vendor's product. The existing CONFIRMD may be safely deleted
- Enhanced the DSPMCHINF command to now show the partition UUID in preparation for future licensing enhancements
- Enhanced the APYLIC command to no longer interactively prompt for license key replacement when the APYLIC command is run as part of a batch job
- Enhanced internal licensing code to reduce unnecessary LICP database file access and reduce necessity of using UPDPROD(*YES) in some situations
- Corrected an issue where WRKRXSRTRE would display an incorrect date when run from a long-running job


## 3.4.7

### Code Generation Tools 
- Enhanced BLDPRS to allow a base envelope value to be specified when generating XML parsing handlers, allowing for shorter XPaths within the handler subprocedure
- Corrected an issue in BLDPRS where code for XML content events would sometimes fail to be generated if some event options were disabled
- Corrected an issue preventing errors triggered within CRTRPGTPL from properly propagating to the user, causing cryptic error messaging when the template creation process failed


### JSON APIs 
- Corrected an issue with the RXS_CreateJson() TrimVariables option incorrectly handling leading whitespace
- Corrected an issue where RXS_GetJsonStringLen() was occasionally returning an incorrect and larger value


### Other Changes 
- The QRPGLECPY,RXSCB copybook is now provided in fully free format RPG. We now also provide a traditional/fixed format RPG equivalent which can be found in QRPGLECPY,RXSCB_FX for RPG developers who are more comfortable with the older RPG code format. Going forward, any changes made to RXSCB will be reflected in RXSCB_FX to the degree that the RPG language allows. RXSCB_FX is intended primarily for reference purposes for developers unfamiliar with fully free format RPG. We don't recommend changing your existing programs to use RXSCB_FX instead of RXSCB, and we don't recommend using RXSCB_FX to write new programs
- Corrected an issue that occasionally caused inconsistent Base64 encoding with RXS_Convert() when the provided input field was not a 4-byte varying field
- Enhanced RXS_XPath() with better error messaging for when string replacements are specified but there are insufficient provided input parameter values to fill them
- Updated RXSURI to be consistent with RXS_Transmit() with regards to trimming input parameters


## 3.4.6

### Conversion APIs 
- [New] Added RXS_ConvertBase64UrlDS_t as option to RXS_Convert() to allow for Base64Url encoding/decoding


### HTTP Communication - RXS_Transmit() 
- Corrected issue with RXS_Transmit() treating HTTP response headers as if they were UTF-8 data instead of ISO-8859-1 as per [RFC 5987](https://tools.ietf.org/html/rfc5987) and [RFC 8187](https://tools.ietf.org/html/rfc8187)
- Corrected issue using custom HTTP headers with RXSURI which could result in the custom headers not being sent


### CGI APIs 
- Corrected issue with RXS_GetUrlVar() that could cause a retrieved URL variable to be truncated instead of retrieved fully


### Code Generation 
- Corrected issue with CRTRPGTPL resulting in a 3029 error when attempting to use a source member for the input instead of an IFS file


### Other Changes 
- Corrected issues with internal licensing code which could cause errors when running on partitions with an LPAR ID above 100


## 3.4.5

### JSON APIs 
- Corrected issue with RXS_ComposeJsonNumber() handling of decimal values that failed to include zeros before the decimal point
- [New] Added new error message KTA120C to RXS_ComposeJsonNumber() to more clearly indicate when a provided input value was unable to be parsed as a valid JSON number


### CGI APIs 
- Corrected issue with RXS_PutStdOut() that could cause a "Pointer not set for location referenced" error to occur when providing the optional data structure parameter. This issue was introduced as part of changes made in RXS 3.4.3


### Other Changes 
- Removed internal license checks from commonly used subprocedures RXS_STR(), RXS_GetJobCCSID(), and RXS_JobLog() to improve overall performance


## 3.4.4

### JSON APIs 
- Corrected issue with using TrimVariables where values composed to child objects/arrays would not be properly trimmed


### RXS_Crypt() 
- Corrected issue with RXS_Crypt() silently failing when not provided with an algorithm when performing SHA hashing. Error KTA0308 will now be thrown


## 3.4.3
### HTTP Communication - RXS_Transmit() 
- Enhanced RXS_TransmitDS_t data structure subfield array CustomHeaderValue to increase the size from 1KB to 4KB to allow for longer custom headers when calling RXS_Transmit()
- Enhanced RXS_HTTPResponseDS_t data structure subfield array HeaderValue to increase the size from 1KB to 4KB to allow retrieving longer headers from the response
- Updated RXS_TransmitDS_t data structure subfield array HeaderCookieFiles to decrease the number of array elements from 50 to 10. This change was based on customer feedback and should help reduce total memory usage


### JSON APIs 
- Updated RXS_ComposeJsonBoolean() to properly treat values other than RXS_JSON_TRUE or RXS_JSON_FALSE as invalid input rather than treating anything other than RXS_JSON_TRUE as 'false'. A KTA120B error will now be thrown when invalid input is provided to RXS_ComposeJsonBoolean()


### Template Variables and Constants 
- [New] Added new RXS_Var4K_t varying 4KB template variable to RXSCB
- [New] Added new RXS_Var1M_t non-varying 1MB template variable to RXSCB


### Other Changes 
- Modified RXS_ResetDS() to increase parameter size to allow for larger data structures
- Corrected text description of `-prdlib` flag in WRKRXSRTRE help text


## 3.4.2
### HTTP Communication - RXS_Transmit() 
- Enhanced RXS_TransmitDS_t data structure with new subfield FollowRedirects to control if RXS_Transmit() will automatically follow HTTP 3XX status code redirects. The default value is `RXS_YES` to maintain compatibility with prior releases
- Enhanced RXS_Transmit() logfile output to indicate that SSLCertStore will use the *SYSTEM certificate store by default


### JSON APIs 
- Enhanced RXS_CreateJsonDS_t with new subfield TrimVariables to control if RXS_ComposeJsonString() will trim excess whitespace from composed JSON values. The default value is `RXS_NO` to maintain compatibility with prior releases
- Corrected issues that could occur when passing JSON names as fields instead of literals when composing JSON
- Corrected issue with RXS_CreateJson() that could cause an `KTA120A: RXS_CreateJson() was provided an invalid data structure parameter` error when using `/DEFINE RXSV6R1`


### STMF APIs 
- Corrected issue with RXS_PutStmf() silently failing when not provided with a filepath


### XML Composition APIs 
- [New] Added RXS_GetComposeBufferLen() API based on customer feedback. This API can be used to retrieve the current length of the compose buffer similar to the functionality of RXS_GetJsonStringLen()


### Other Changes 
- Corrected text description of PORT parameter on CRTRXSSVR command


## 3.4.1
### RXS Router 
- Corrected issues preventing RXS Router `-passwd` flag from working correctly when the QPWDLVL system value was set to 2 or 3
- Enhanced RXS Router `-passwd` flag to support passwords up to 128 characters and removed character restrictions
- Enhanced `-passwd` flag for RXS Router to support `*NOPWD`, `*NOPWDSTS`, and `*NOPWDCHK` to allow profile swapping to IBM i user profiles with their password set to `*NONE`
- Enhanced RXS Router to send a message to the CGI joblog on start to show which version of RPG-XML Suite is being used
- Enhanced RXS Router debug report page to show which version of RPG-XML Suite is being used


### HTTP Communication - RXS_Transmit() and RXS_getUri() 
- Updated RXSURI command to internally call RXS_Transmit() instead of RXS_getUri(). The log file generated by RXSURI now reflects the RXS_Transmit() log format as a result
- Updated default values specified in the RXSURIP file used by the RXSURI command. Requests will now use HTTP 1.1 and CCSID 1208 by default. These values replace the defaults of HTTP 1.0 and CCSID 819 used in previous versions
- Corrected issue which could cause intermittent SSL timeout errors to appear in the RXS_Transmit() log file in rare cases


### RXS_Crypt() 
- [New] Added support for AES-128, AES-192, and AES-256 encryption to RXS_Crypt()


### RXS_Convert() 
- Corrected issue with unexpected output when using RXS_Convert() to perform URL percent encoding where multibyte characters were present
- Enhanced RXS_ConvertURLPercentDS_t with two new subfields, InputCCSID and OutputCCSID, both of which are defaulted to the CCSID of the current job
- Corrected issue which could occur rarely when using RXS_Convert() to perform CCSID conversion with empty input


### JSON APIs 
- Corrected issue with how the JSON compose APIs handle CCSID conversion of some non-printable characters to UTF-8, which could result in incorrect output of multibyte characters after conversion


### XML Composition APIs 
- Corrected issue introduced in 3.4.0 which could cause RXS_GetComposeBuffer() to retain previously retrieved composed data


### XML DOM Parsing APIs 
- Corrected issue with the RXS3 DOM APIs which could prevent loading XML documents larger than 4MB


### XML Event Parsing APIs 
- Corrected issue introduced in 3.4.0 which caused RXS2 RXS_parse() error messages to be partially returned in an incorrect CCSID



## 3.4.0
- **The minimum supported operating system version is now IBM i 7.1**
- RPG-XML Suite's version number now uses the format MAJOR.MINOR.PATCH to be more in line with commonly used semantic versioning standards
- The RXS 2 and RXS 3 APIs are no longer separately versioned as they were in previous releases. Output provided by the DSPVER and DSPMCHINF commands as well as the VERRXSBASE program will now only report a single version number


### Example Programs 
- Example source code for the RXS 2 APIs is no longer provided in the included EXAMPLE source physical file. We recommend all new applications are written using the RXS 3 APIs. Example code for the RXS 3 APIs has been moved to the EXAMPLE source physical file
- Example programs are no longer provided in a compiled state during the installation. This change was made based on customer feedback


### RXS 2 Copybook 
- The RXSCP copybook containing the RXS 2 API prototypes, data structures, and constants which was previously located in the included RXS source physical file has been moved to the included QRPGLECPY source physical file. Existing programs using the RXS 2 APIs will continue to work without modifications, but before any future recompilation you will need to change the `/copy RXS,RXSCP` line in your programs to be `/copy QRPGLECPY,RXSCP`


### HTTP Communication - RXS_Transmit() and RXS_getUri() 
- Changed RXS_Transmit() and RXS_getUri() to use the IBM GSKit SSL APIs for SSL/TLS communication. This allows for SNI support and other improved capabilities. Note that this may cause RXS_TransmitDS_t.SSLVerifyPeer and RXS_TransmitDS_t.SSLVerifyHost subfields to no longer ignore missing/invalid SSL certificates in all circumstances, and you may begin receiving errors as a result after upgrading. If you do, you should [install the SSL certificates](https://isupport.katointegrations.com/rxs/installing_certificate_authorities.md) for the endpoint you are calling
- Corrected issues with RXS_Transmit() which could cause a generated log file to be empty
- Enhanced the RXS_TransmitDS_t data structure to include a new LocalInterface subfield. LocalInterface can be set to an IP address. When set to an IP address, RXS_Transmit() will use this value to determine the proper network interface to use.
- Enhanced the RXS_TransmitDS_t data structure to include a new EnableIPv6 subfield. EnableIPv6 can be set to RXS_YES or RXS_NO (default: RXS_NO). When set to RXS_YES this option will allow RXS_Transmit() to resolve a host name to either the IPv4 or IPv6 address instead of being limited to only resolving to the IPv4 address.
- Corrected issue with RXS_Transmit() when performing a HTTP DELETE operation
- Corrected issue preventing RXSURI command from properly sending custom HTTP headers in some circumstances


### JSON APIs 
- Enhanced JSON parsing and composing API internal code, which corrects a number of issues with memory usage and CCSID handling as well as JVM conflicts. Any existing programs that are parsing or composing JSON should perform substantially better
- Previously, the JSON APIs relied on files in the IFS stored in a /krengeltech directory. This directory is no longer required starting with RXS 3.4.0 and can be safely removed
- Calling RXS_DestroyJson() is no longer required after RXS_ParseJson(). RXS_DestroyJson() now only needs to be called on the RXS_CreateJsonDS_t that was originally used in a call to RXS_CreateJson(). Existing code which calls RXS_DestroyJson() after RXS_ParseJson() will continue to function without any changes or recompilation required, but we recommend reviewing your code to remove any unnecessary RXS_DestroyJson() calls
- [New] Added RXS_FormatJson() API based on customer feedback. This API can be used to format a JSON document or STMF to either minify the document, or to expand it and control indentation and whitespace
- [New] Added RXS_GetJsonStringLen() API based on customer feedback. This API will return the length of character field needed to store the JSON document composed in the provided RXS_CreateJsonDS_t
- Enhanced JSON APIs to support duplicate keys within objects per JSON RFC 7159
- Enhanced RXS_ComposeJsonString() to allow *Omit for parameter 2. *Omit is treated as equivalent to passing a 0 length empty string
- Enhanced error messages for all RXS JSON composition and parsing APIs to provide more detail
- Enhanced validation for RXS_ParseJson() settings. As a result, programs that are calling RXS_ParseJson() without specifying a parsing handler will now throw error message KTA1207
- Enhanced validation for input provided to RXS_ParseJson(). As a result, programs that are calling RXS_ParseJson() and providing an empty JSON document will now throw error message KTA1208
- Corrected issue when using RXS_ParseJson() to parse a JSON document which contained empty child JSON objects


### XML Composition APIs 
- Enhanced the RXS_ComposeDS_t data structure to include a new TrimVariables subfield. TrimVariables can be set to RXS_YES or RXS_NO (default: RXS_NO). When set to RXS_YES this option will cause RXS_ComposeVariable() to remove all leading and trailing whitespace from the passed value equivalent to if the value had been wrapped by the RPG %Trim() built-in function
- Enhanced the RXS_ComposeDS_t data structure to include a new EncodeVariables subfield. EncodeVariables can be set to RXS_YES or RXS_NO (default: RXS_NO). When set to RXS_YES this option will cause RXS_ComposeVariable() to encode all XML reserved characters (`&` `'` `"` `>` `<`) with the appropriate entities (`&amp;` `&apos;` `&quot;` `&gt;` `&lt;`) in the passed value
- Enhanced RXS_ComposeVariable() to allow `*Omit` to be passed for parameter 2. `*Omit` is treated as equivalent to passing a 0 length empty string
- Corrected issue preventing RXS_ComposeVariable(), RXS_ComposeSection(), and RXS_GetComposeBuffer() from throwing an error when RXS_StartComposeEngine() had not previously been called


### RXS_Convert() 
- Corrected issue when using RXS_Convert() to encode XML entities that could lead to a memory leak in certain scenarios
- Corrected issue causing RXS_Convert() to throw an error when provided with an empty character field for input


### RXS_Crypt() 
- Corrected issue preventing `*Omit` from being used as the first parameter for RXS_Crypt()
- Corrected issue with RXS_Crypt() MD5 and SHA hashing memory usage and IFS file access


### Code Generation 
- Enhanced the BLDPRS command to support generating RXS 2 XML parsing code, RXS 3 XML parsing code, or JSON parsing code. BLDPRS can now also generate either mixed or column-limited full free format RPG. As part of this update, the names of some existing parameters were changed and new parameters were added
- Enhanced the BLDTPL command to provide better CCSID handling and performance. As part of this update, the names for some parameters have been changed
- Enhanced the BLDTPL command to no longer remove the XML prolog during template generation based on customer feedback
- Enhanced the CRTRPGTPL command to write a comment section in the generated template member that contains the full command used to generate the template for future reference


### Other Changes 
- [New] Added RXS_RenameStmfDS_t as option to RXS_ProcessStmf() to allow for easier IFS file renaming
- Corrected intermittent error message caused by RXSRTR appearing in Apache job log
- Corrected issue preventing RXS_GetUrlVar() from handling empty URL variables properly
- All included commands now provide help via included *PNLGRP objects


## 3.36
- Internal changes to allow initial SNI support.


## 3.35
- Corrected an issue with ScriptAliasMatch directives when building a new httpd.conf file.


## 3.34
- RXS_Convert() now works properly when URL encoding/decoding strings over 64K


## 3.33
- RXS_GetJsonString() now works properly in all situations with JSON prettification disabled.
- RXS_RESETDS() now works correctly with RXS_CreateJsonDS_t, RXS_JsonStructureDS_t, RXS_ParseJsonDS_t
- RXS_GetEnvVar() now correctly returns an empty string for variables which do not exist
- RXS_Parse() no longer requires programs compiled under v3.31 or earlier to be recompiled to avoid a decimal data error. This affected only programs running under v3.32 after last being compiled with v3.31 or earlier.


## 3.32
- RXS_ResetDS() now supports: RXS_DS_TYPE_CREATEJSON, RXS_DS_TYPE_JSONSTRUCTURE and RXS_DS_TYPE_PARSEJSON
- RXS_GetStdIn() now properly handles binary output to a stream file
- Performance enhancements to RXS_Parse()
- Performance enhancements to RXS_ComposeVariable() and RXS_ComposeSection()
- Improvements to RXS_OpenDom() to prevent XML declarations from conflicting with the DOM parser's internal character set handling
- Improvements to error handling in RXS_Throw() and RXS_Catch()
- Improved memory handling and performance in RXS_Convert()
- Improvements to error reporting in RXS_OpenDom(), RXS_Parse() and RXS_Convert()
- Improvements to RXS_GetUrlVar() and RXS_GetStdIn() when processing URL encoded content
- Improved BLDTPL to output variable names that conform to RPG's naming rules
- Performance enhancements to speed licensing functions
- RXS_ParseJson() prototype changed to use Const instead of Value. **All programs parsing JSON will need to be updated and recompiled.**


## 3.31
- Improved error reporting when parsing a JSON string with incorrect syntax
- Improved error reporting when processing JSON without authority to required IFS objects
- Correction to RXS_ComposeJsonBoolean() so that composed values are correct
- Improved memory handling when composing very large XML strings
- Improved performance when parsing large stream files
- Improved support for calling non-CGI programs from RXSRTR function


## 3.30
- [New] Added JSON parsing API [RXS_ParseJson()](https://isupport.katointegrations.com/rxs/3.3/rxs_parsejson.md)
- [New] Added JSON composition APIs:
  - [RXS_CreateJson()](https://isupport.katointegrations.com/rxs/3.3/rxs_createjson.md)
  - [RXS_DestroyJson()](https://isupport.katointegrations.com/rxs/3.3/rxs_destroyjson.md)
  - [RXS_ComposeJsonNull()](https://isupport.katointegrations.com/rxs/3.3/rxs_composejsonnull.md)
  - [RXS_ComposeJsonString()](https://isupport.katointegrations.com/rxs/3.3/rxs_composejsonstring.md)
  - [RXS_ComposeJsonBoolean()](https://isupport.katointegrations.com/rxs/3.3/rxs_composejsonboolean.md)
  - [RXS_ComposeJsonNumber()](https://isupport.katointegrations.com/rxs/3.3/rxs_composejsonnumber.md)
  - [RXS_ComposeJsonObject()](https://isupport.katointegrations.com/rxs/3.3/rxs_composejsonobject.md)
  - [RXS_ComposeJsonArray()](https://isupport.katointegrations.com/rxs/3.3/rxs_composejsonarray.md)
  - [RXS_GetJsonString()](https://isupport.katointegrations.com/rxs/3.3/rxs_getjsonstring.md)


## 3.22
- Corrected a problem with multi-byte characters in request data causing a false timeout condition when calling RXS_Transmit()
- Corrected a problem with multi-byte characters in XML data causing RXS_Validate() to fail when it should have been successful
- Changes to the Compose engine to correctly handle repeating data when using the OmitUncomposedLines option with multiple calls to RXS_ComposeSection()
- Improvement to RXS_ComposeVariable() to allow `*BLANKS` to be passed on the second parameter, without composing 16MB's of blanks/spaces and instead the composed content will be the same as if a zero length value had been passed


## 3.21
- Corrected an issue that required programs using RXS_Transmit() to be recompiled. Recompiling is no longer needed
- Improvements to CRTRPGTPL to better handle templates that have invalid formatting or characters that are not allowed in RPG variables names


## 3.20
- RESTful HTTP Methods such as PUT, DELETE, HEAD, OPTIONS and PATCH (POST and GET continue to be available)
- Easy parsing of content from RESTful URI’s such as http://www.example.com/customer/53874
- Support for dynamic as well as compiled templates
- Extended length templates up to 256K
- Automatic compression of whitespace when composing from templates
- Automatic omission of uncomposed template content
- New function RXS_ResetDS() for easily initializing data structures used by the RXS3 API
- Enhanced RXS_GetStmf() to allow easy reading of stream file content in “chunks” within a loop
- Enhanced RXS_PutStdOut() for easy output of HTTP Status, Content Type and other HTTP headers
- Extended support for very large XML stream files when using the event based parser
- Improvements to CRTRPGTPL command
- Read STDIN over 16MB directly to a stream file
- Output large stream files over 16MB in size to STDOUT
- Improved logging from RXS_Transmit()
- Improved RXS_Transmit() when using Basic Authority so that only one request is made to the remote server
- Improved STMF handling to inherit authority from the host directory when creating new STMFs


## 3.11
- Introduced [RXS_Validate()](https://isupport.katointegrations.com/rxs/3.1/rxs_validate.md) for use with XSD Validation


## 3.10
- Full support for XPath 1.0 compliant Parsing
- [New] Added DOM parsing APIs:
  - [RXS_OpenDom()](https://isupport.katointegrations.com/rxs/3.1/rxs_opendom.md)
  - [RXS_CloseDom()](https://isupport.katointegrations.com/rxs/3.1/rxs_closedom.md)
  - [RXS_ParseDomToText()](https://isupport.katointegrations.com/rxs/3.1/rxs_parsedomtotext.md)
  - [RXS_ParseDomToXml()](https://isupport.katointegrations.com/rxs/3.1/rxs_parsedomtoxml.md)
  - [RXS_ParseDomToDom()](https://isupport.katointegrations.com/rxs/3.1/rxs_parsedomtodom.md)


## 3.00
- **The minimum supported operating system is now IBM i 6.1**
- Completely new RXS3 API introduced. This API has been redesigned from the ground up and hands the RPG developer an even more powerful tool.
- Full support for 16 MB in-memory variables
- Base 64 encoding
- Full UTF-8 support and improved CCSID handling
- Enhanced data security


## 2.88
- Improved compatability with proxy configuration in previous versions.
- Improved availability of joblog messages in CGI jobs running via RXSRTR and also using activation groups


## 2.87
- Expanded number of variables allowed in one section of a template
- Allowing only IPv4 when using RXS_getUri(). IPv6 will require use of RXS_Transmit()


## 2.86
- Resumed support for LocalIP and LocalPort settings when calling RXS_getUri()
- Improved backwards compatability when using RXS_getUri() with HTTP Get method


## 2.85
- Improved support for conversion of XML entities during parsing


## 2.84
- Improved parsing of content that contains XML entities in combination with multi-byte character sets such as UTF-8
- New options for RXS_getUri() that allow host and peer verification to be turned off


## 2.83
- Improved STMF handling to inherit authority from the host directory when creating new STMF's
- Improved RXS_getUri() to handle URI's that do not start with http or https
- Improved RXS_getUri() when using Basic Authority so that only one request is made to the remote server


## 2.82
- Improvements to CCSID handling when outputting to existing stream files with multi-byte character sets


## 2.81
- Correction to template engine when outputing stream files encoded as UTF-8 or other multi-byte character sets


## 2.80
- Enhancements to BLDPRS command
- Ignore default namespaces in DOM parser unless DOM_INCNSATR set as an option
- Updates to HTTP client to correct output to debug files when multiple communications are transmitted
- Created RXSMENU, DSPVER, DSPLIC and APYLIC commands for menu driven version and licensing tasks
- Seperate licensing library KTLIC no longer required


## 2.71
- Enhancement to allow multiple members in RXSCFG to allow member names to reflect RXSRTR environment names when desired. (The RXSCFG member is the default when RXSRTR is not in use or a member matching the environment name is not found.)
- When creating Apache instances, changed logs directory authority to `*RWX`
- Licensing updates for certain IBM serial numbers and for BLDTPL and BLDPRS commands


## 2.70
- **The minimum supported operating system is now IBM i V5R4**
- Enhancements to RXS Router allowing single library installation for simplified configuration and streamlined upgrades.
- Improved CCSID handling to support character sets outside of North America
- Various performance improvements and bug fixes
- New licensing system
- New [RXS_getUri()](https://isupport.katointegrations.com/rxs/2.x/rxs_geturi.md) communications client


## 2.51
- Correction to allow case insensitive XPath's when using the DOM parser


## 2.50
- Enhancements to RXS Router allowing single library installation for simplified configuration and streamlined upgrades. Improved CCSID handling to support character sets outside of North America


## 2.40
- Fixed bug in DOM parser to intialize storage used in previous parses
- Improved limits in DOM parser allowing increased element count in parsed XML
- Enhanced RXS Router to allow mixed case for RXSRTRCTL option switches
- Fixed bug in [RXS_getUrlVar()](https://isupport.katointegrations.com/rxs/2.x/rxs_geturlvar.md) to allow encoded ampersands to be decoded correctly (%26)
- Enhancement to [RXS_getUrlVar()](https://isupport.katointegrations.com/rxs/2.x/rxs_geturlvar.md) to decode percent encoded characters correctly when CGIConvMode is set to MIXED
- Fixed parser bug to allow UTF-8 double-byte characters in attribute data


## 2.30
- Fixed bug in [RXS_getUrlVar()](https://isupport.katointegrations.com/rxs/2.x/rxs_geturlvar.md) when the last variable's value in QUERY_STRING was a single character.
- Fixed bug where [RXS_getUrlVar()](https://isupport.katointegrations.com/rxs/2.x/rxs_geturlvar.md) was returning the **&** char in the event of an empty QUERY_STRING variable.


## 2.20
- Enhancement: Options used when calling [RXS_DOMSetOpt()](https://isupport.katointegrations.com/rxs/2.x/rxs_domsetopt.md) or [RXS_DOMBuild()](https://isupport.katointegrations.com/rxs/2.x/rxs_dombuild.md) have been simplified.
- Fixed bug: Modified the template engine to ignore other forms of whitespace at the end of sections names. Previously, if invisible characters existed due to copying template code from Word or PDF documents, the section name would not be detected properly and would require the template to be modified.
- Modified RXS Router added in v2.10 to exist in base RXS library, rather than a separate library.
- Modified RXS Router command INZRXSRTR to be executable by users with authority to RXSRTRCTL and INZRXSRTR.
- Modified RXS Router httpd.conf to allow multiple types of CGI invocations. This allows you to configure URLs which bypass the router if needed.
- Fixed bug where DOM Parser functions [RXS_DOMGetData()](https://isupport.katointegrations.com/rxs/2.x/rxs_domgetdata.md) and [RXS_DOMGetDataCount()](https://isupport.katointegrations.com/rxs/2.x/rxs_domgetdatacount.md) would return incorrect values when using repeating elements.
- Fixed bug where sometimes during install, data area RXSBASE was not being given appropriate authority, and would falsely report that an RPG-XML Suite license key was invalid immediately after installation.


## 2.10
- NEW FEATURE: Added new "RPG-XML Suite Router" functionality. This will ease the offering of web services as it takes care of altering the library list and user profile before your web service is invoked.
- NEW FEATURE: RXS HTTP Command to make it easier, and safer, to end, start, and configure your Apache server instances.
- Enhancement: When processing template files (i.e. IFS .tpl files), the section names will have `*Blanks` trimmed from the end of their name. Previously, if blanks existed, the template engine wouldn't consider the name and the .tpl would have to be modified.
- Enhancement: Allow [RXS_getEnvVar()](https://isupport.katointegrations.com/rxs/2.x/rxs_getenvvar.md) to be called multiple times. Normally in the case of GET based web services there has been a single URL parameter named "xml" that would contain the inbound XML. With RESTFul web services making a mark in the web services world we have expanded the RXS_getEnvVar() API to facilitate multiple calls for differently named variables.
- Enhancement: Traditionally when there were unmatched delimiters in a template file (i.e. IFS .tpl files) it would throw a MCH0603 error. Now a more informative message will be relayed to the job log.
- Enhancement: Speed improvements have been made in the RXS_DOM* based parsing API's.


## 2.00
- NEW FEATURE: Added DOM features for simplified parsing of XML documents. New APIs include:
  - [RXS_DOMBuild()](https://isupport.katointegrations.com/rxs/2.x/rxs_dombuild.md)
  - [RXS_DOMGetData()](https://isupport.katointegrations.com/rxs/2.x/rxs_domgetdata.md)
  - [RXS_DOMGetDataCount()](https://isupport.katointegrations.com/rxs/2.x/rxs_domgetdatacount.md)
  - [RXS_DOMCleanup()](https://isupport.katointegrations.com/rxs/2.x/rxs_domcleanup.md)
  - [RXS_setXPath()](https://isupport.katointegrations.com/rxs/2.x/rxs_setxpath.md)
  - [RXS_getXPath()](https://isupport.katointegrations.com/rxs/2.x/rxs_getxpath.md)


## 1.42
- Fixed bug where RXSCFG was being read for each call to RXS_getUri() or RXS_initTplEng() and now it is just called the first time in each job and then the data is cached to better performance.
- Fixed bug that was causing issues when RXS was used in conjunction with DDM files or RPG being front ended by stored procedures. This was a bug that affected few customers in very specific usage scenarios.


## 1.41
- Modified RXS_getUri() to use 65535 VARYING vs. 65535 without VARYING. This addresses issues when trying to pass by reference and not having the exact same string type.
- RXSCP fields RXS_getUriOut, RXS_getUriHead, and RXS_getUriData have been deprecated and commented out. They will be physically removed from RXSCP in v1.5. If you had version 1.4 installed you will need to recompile your programs to use this latest version.
- Modified RXS_getUri() to default to port 443 if port is 0 and SSL=RXS_YES was specified.


## 1.40
- NEW FEATURE: RXS_setParseEnc()
- NEW FEATURE: RXS_ignElemNamSpc()
- Modified commands BLDPRS and BLDTPL to better handle long XPaths
- Modified default delimiters to be `::` for section names and `.:var:.` for variable names. In previous versions they were `/$` for section names and `/%var%/` for variable names. This was changed for variety of reasons with the biggest being usage in foreign countries. By using colons and periods the code can be typed faster for manual modification of templates. If you are upgrading you should do an UPDDTA RXSCFG and change the defaults to `/$` for section begin delimiter, `/%` for var begin delimiter and `%/` for var end delimiter.


## 1.30
- NEW FEATURE: Added RXS_addLibLE()
- NEW FEATURE: Added RXS_libLEExists()
- NEW FEATURE: Added RXS_rmvLibLE()
- NEW FEATURE: Added RXS_getBuffLen() to get a count of bytes in the Template Engine buffer to know if it is over 65535 and thus to big to retrieve with RXS_getBuffData().
- NEW FEATURE: Added RXS_getBuffData() to get the Template Engine data that is currently buffered so it can be put into a 65535 VARYING field and used on RXS_getUri().
- Changed HTTPD.txt to not use $ signs (also changed NEWENV command)
- Fixed bugs in BLDPRS that were making it not work right with long xPaths.
- [New] Added delim fields to RXSCFG
- Changed RXS_initTplEng() to operate off of default delimiters in RXSCFG and change default delimiters in program to not be constants.
- Changed RXS_updVar() to be able to do 65535 varying, was set at 1024 at an inner level of code.
- Enhancing RXS_soapDecode() for speed and accuracy.
- Adding code to BLDPRSR and BLDTPLR to not delete user index unless it exists. It was putting messages in job log uncessarily
- [New] Added DftTransDir to RXS_readToFile() if no path was specified.
- [New] Added DftTransDir to RXS_outFromFile() if no path was specified.
- [New] Added DftTransDir to RXS_deleteFile() if no path was specified.
- When using RXS_readToFile() extra spaces were sometimes added to the end of the files contents. This has been addressed to not have additional spaces.
- Change error text in RXS_cmpTransFile() to be RXSCFG instead of CONFIG.
- [New] Added defaults for RXS_getUri(): This will save typing when using RXS_getUri() as not as many data structure fields will need to be filled.
  - ds.ReqType=RXS_POST
  - ds.SprHead=RXS_YES
  - ds.Debug=RXS_NO
  - ds.RspType=RXS_VAR
  - ds.ReqType=RXS_VAR
  - ds.ContType='text/xml'
- When a POST with zero content is sent and RXS_readToFile() is used an error was inappropriately thrown. Now no error is thrown.
- Changed RXS_readToFile() to truncate content on IFS open.
- Changed RXS_getUriOut and RXS_getUriHead from 32767 to 65535 in RXSCP (main RXS copybook)
- [New] Added field RXS_getUriData to RXSCP in relation to new RXS_getUri() functionality
- Changed RXS_getUri() to be able to send and receive 65535 bytes of data if not using IFS files. Previous limitation was 2048.
- [New] Added new OutType of RXS_VAR to RXS_initTplEng(). This should be used in conjunction with RXS_getBuffData().
- **!!RECOMPILE!!** Changed RXS_getUri() to have the second, third and newly added forth parms as `OPTIONS(*OMIT)` for ease of use (so you don't have to specify them if you aren't using them)
- [New] Added example TPLENG3 to show how to use RXS_getBuffData().
- The field RXS_GetUriHead (i.e. response HTTP Headers) will always be returned when a variable is specified vs. only being returned when SprHead (Seperate Headers) is specified. This pertains to RXS_getUri().
- Modified client app Web Service Tester to save responses to a stream file and retain URLs that have been accessed.


## 1.20
- Renamed RPG-XML Suite config PF, MYRXS/CONFIG, to MYRXS/RXSCFG.
- Changed RXS_parse() to `%TRIM` input file name
- Changed RXS_parse() to throw an error if file to parse doesn't exist.
- [New] Added RXS_soapDecode()
- Increased RXS_updVar() input to 65535.
- Changed RXS_initTplEng() to `%TRIM` out file name
- [New] Added example GETURI4 to show Template Engine overriding of sections/variables and also ability to override event handler type values.
- [New] Added RXS_charToTimestamp()
- [New] Added RXS_timestampToChar()
- [New] Added RXS_charToBln()
- Modified RXS_charToNbr() to have default value parameter.
- [New] Added parsing code generator BLDPRS(Build RPG Parsing Subprocedure)
- [New] Added template generator BLDTPL (Build Template)
- [New] Added RXS_getUri() PUser and PPW (Proxy User and Proxy Password)
- [New] Added DSPMCHINF for ease of displaying machine information.


## 1.10
- Changed RXS_loadTpl() to allow overriding abilities for section and variable delimiters. For internationalization purposes.
- Changed RXS_parse() to allow the passing of handler event type value overrides (i.e. ELEMBEGIN, ELEMCONTENT, ELEMEND, ATTR). For internationalization purposes.
- [New] Added example RXS6 to show Template Engine overriding of sections/variables and also ability to override event handler type values.
- Modified install *SAVF to be V5R1 instead of V5R3


## 1.00
- Initial release.

---

# RXS_Catch()

> Catches an RXS exception thrown by RXS_Throw(), returning error details for structured error handling.

This subprocedure can be used to catch an error thrown by [RXS_Throw()](https://isupport.katointegrations.com/rxs/3.3/rxs_throw.md) or via other methods. The error information is retrieved into a RXS_CatchThrowErrorDS_t data structure.

Typical usage involves this subprocedure being called inside the ON-ERROR section of a MONITOR group.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns RXS_CatchThrowErrorDS_t data structure containing the error
      //   information that was previously thrown.
     D RXS_Catch...
     D                 PR                  Extproc('RXS_Catch')
     D                                     Likeds(RXS_CatchThrowErrorDS_t)
```


## Example Code

### IBM i 6.1+

#### Example 1: Catch Error

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates calling RXS_Catch() and assigning the return
      *  value to a RXS_CatchThrowErrorDS_t datastructure.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*NEW)

      /COPY QRPGLECPY,RXSCB

     D ErrorDS         DS                  LikeDS(RXS_CatchThrowErrorDS_t)
     D                                     Inz(*LikeDS)

     D NumA            S             10I 0
     D NumB            S             10I 0
     D Result          S             10I 0
      /FREE
       monitor;
         // To ensure we trigger an error, we're going to divide by 0.
         NumA = 5;
         NumB = 0;
         Result = NumA / NumB;
       on-error;
         // Note that you don't have to use RXS_ResetDS to initialize ErrorDS before
         //  using it - this is one of the few RXS data structures where
         //  this is safe and correct.
         ErrorDS = RXS_Catch();

         // At this point ErrorDS should contain the error information for an
         //  MCH1211 "divide by zero" error.
       endmon;

       return;
      /END-FREE
```

## Data Structures

### RXS_CatchThrowErrorDS_t

```rpgle
     D RXS_CatchThrowErrorDS_t...
     D                 DS                  Qualified Template Inz

      // The message ID that was received.
     D   MessageId                    7A

      // The message file used to store the message ID.
     D   MessageFile                 20A

      // The message data to be merged with the text of the message ID.
     D   MessageData               1024A   Varying

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CATCHTHROWERROR)

      // The type of message that was sent.
      // Valid values: RXS_MESSAGE_TYPE_DIAG, RXS_MESSAGE_TYPE_COMP,
      //   RXS_MESSAGE_TYPE_INFO, RXS_MESSAGE_TYPE_INQ, RXS_MESSAGE_TYPE_RQS,
      //   RXS_MESSAGE_TYPE_NOTIFY, RXS_MESSAGE_TYPE_ESCAPE,
      //   RXS_MESSAGE_TYPE_STATUS
     D   MessageType                  5I 0

      // The full message text that combines the value of the message ID's text
      //   and the merged message data.
     D   MessageText               4096A   Varying

      // Not used. Used by RXS_Throw() only.
     D   ThrowToCaller...
     D                                 N   Inz(RXS_NO)
```



---

# RXS_CloseDom()

> Frees memory allocated by RXS_OpenDom(); must be called once DOM parsing is complete.

This subprocedure is used to clean up after utilizing the DOM parsing API [RXS_OpenDom()](https://isupport.katointegrations.com/rxs/3.3/rxs_opendom.md). It only needs to be called on the primary DOM data structure. You should not attempt to close a DOM data structure created as a result of calling RXS_ParseDomToDom().

This subprocedure must be called at the end of all DOM parsing to properly free up memory and avoid performance impacts.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_CloseDom    PR                  Extproc('RXS_CloseDom') Opdesc

      // The RXS_ParseDomDS_t data structure previously initialized and used as
      //   part of RXS_OpenDom()
     D  DS                                 Like(RXS_ParseDomDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 6.1+

#### Example 1: Closing DOM Parsing

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates closing DOM parsing using a RXS_ParseDOMDS_t
      *  datastructure.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D OpenDomDS       DS                  LikeDS(RXS_OpenDOMDS_t)
     D RootDomDS       DS                  LikeDS(RXS_ParseDOMDS_t)
      /free
       RXS_ResetDS( OpenDomDs : RXS_DS_TYPE_OPENDOM );
       RXS_ResetDS( RootDomDs : RXS_DS_TYPE_PARSEDOM );
       OpenDomDs.InputCcsid = RXS_CCSID_UTF8;
       RootDomDS = RXS_OpenDom( gXmlResponse : OpenDomDs );
       
       gXPath = RXS_XPath( '/*:Envelope/*:Body/*:FahrenheitToCelsius' +
         'Response/*:FahrenheitToCelsiusResult' );
       
       gCelsius = %Dec( RXS_ParseDomToText( gXPath : RootDomDS ) : 11 : 7 );
       
       RXS_CloseDom( RootDomDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_ParseDomDS_t

```rpgle
     D RXS_ParseDomDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specifies the CCSID of the XML being parsed.
     D   InputCcsid...
     D                               10I 0

      // Specifies the CCSID the parsed data will be converted to.
     D   OutputCcsid...
     D                               10I 0

      // Specifies an IFS path to an XML file to parse instead of the Input parm.
     D   Stmf...
     D                                     Like(RXS_Var1Kv_t)

      // Contains the current count of XML nodes tracked by this data structure.
     D   NodeCount                   10U 0

     D   NodeType                    10I 0

      // Internal use only
     D   xmlPtr                        *

      // Internal use only
     D   docPtr                        *

      // Internal use only
     D   contextPtr                    *

      // Internal use only
     D   dictPtr                       *

      // Internal use only
     D   currentPtr                    *

      // Internal use only
     D   parentPtr                     *

      // Internal use only
     D   Reserved                  4096A
```



---

# RXS_ComposeJsonArray()

> Adds a JSON array element to a parent JSON object or array.

This subprocedure is used to add a JSON array to a parent JSON Object or Array.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeJsonArray...
     D                 PR                  Extproc('RXS_ComposeJsonArray')
     D                                     Opdesc
     D                                     LikeDS(RXS_JsonStructureDS_t)

      // Name to assign to JSON array. If being added to an array, the value of
      //   this parameter should be *OMIT.
     D  pName                              Const Like(RXS_Var64Kv_t)
     D                                     Options(*Omit:*Varsize)

      // RXS_JsonStructureDS_t data structure containing a parent JSON Object or
      //   Json Array.
     D  pStructureDS                       Const LikeDS(RXS_JsonStructureDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create JSON object with child array

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a child array 
      * to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        ChildDS = RXS_ComposeJsonArray( 'child' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "child": [] }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple child arrays

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 3 child arrays 
      * to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 3;
          ChildDS = RXS_ComposeJsonArray( *OMIT : RootDS );
          RXS_ComposeJsonString( *OMIT : %Char(i) : ChildDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // [["1"],["2"],["3"]]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create JSON object with child array

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a child array 
      * to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        ChildDS = RXS_ComposeJsonArray( 'child' : RootDS );

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // { "child": [] }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple child arrays

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 3 child arrays 
      * to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 3;
          ChildDS = RXS_ComposeJsonArray( *OMIT : RootDS );
          RXS_ComposeJsonString( *OMIT : %Char(i) : ChildDS );
        endfor;

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // [["1"],["2"],["3"]]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_ComposeJsonBoolean()

> Adds a JSON boolean element (true/false) to a parent JSON object or array.

This subprocedure is used to add a JSON boolean element to a parent JSON Object or Array.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeJsonBoolean...
     D                 PR                  Extproc('RXS_ComposeJsonBoolean')
     D                                     Opdesc

      // Name to assign to JSON boolean data element. If being added to an array,
      //   the value of this parameter should be *OMIT.
     D  pName                              Const Like(RXS_Var64Kv_t)
     D                                     Options(*Omit:*Varsize)

      // Pass RXS_JSON_TRUE if the JSON boolean data element should be true,
      //   RXS_JSON_FALSE if the JSON boolean data element should be false.
     D  pBoolean                       N   Const

      // RXS_JsonStructureDS_t data structure containing a parent JSON Object or
      //   Json Array.
     D  pStructureDS                       Const LikeDS(RXS_JsonStructureDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create JSON object with boolean element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a boolean 
      * data field to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonBoolean( 'value' : RXS_JSON_TRUE : RootDS );
        RXS_ComposeJsonBoolean( 'enabled' : RXS_JSON_FALSE : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "value": true, "enabled": false }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple boolean elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 5 boolean data 
      * fields to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonBoolean( *OMIT : RXS_JSON_TRUE : RootDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // [true,true,true,true,true]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create JSON object with boolean element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a boolean 
      * data field to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonBoolean( 'value' : RXS_JSON_TRUE : RootDS );
        RXS_ComposeJsonBoolean( 'enabled' : RXS_JSON_FALSE : RootDS );

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // { "value": true, "enabled": false }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple boolean elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 5 boolean data 
      * fields to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonBoolean( *OMIT : RXS_JSON_TRUE : RootDS );
        endfor;

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // [true,true,true,true,true]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_ComposeJsonNull()

> Adds a JSON null element to a parent JSON object or array.

This subprocedure is used to add a JSON **null** element to a parent JSON Object or Array.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeJsonNull...
     D                 PR                  Extproc('RXS_ComposeJsonNull')
     D                                     Opdesc

      // Name to assign to JSON null data element. If being added to an array,
      //   the value of this parameter should be *OMIT.
     D  pName                              Const Like(RXS_Var64Kv_t)
     D                                     Options(*Omit:*Varsize)

      // RXS_JsonStructureDS_t data structure containing a parent JSON Object or
      //   Json Array.
     D  pStructureDS                       Const LikeDS(RXS_JsonStructureDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create JSON object with null element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a null data 
      * field to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonNull( 'value' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "value": null }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple null elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 5 null data 
      * fields to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonNull( *OMIT : RootDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // [null,null,null,null,null]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create JSON object with null element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a null data 
      * field to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonNull( 'value' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "value": null }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple null elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 5 null data 
      * fields to it, retrieves the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonNull( *OMIT : RootDS );
        endfor;

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // [null,null,null,null,null]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_ComposeJsonNumber()

> Adds a JSON numeric element to a parent JSON object or array.

This subprocedure is used to add a JSON number element to a parent JSON Object or Array. The numeric data must be passed as an RPG character field, or converted by %Char().

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeJsonNumber...
     D                 PR                  Extproc('RXS_ComposeJsonNumber')
     D                                     Opdesc

      // Name to assign to JSON numeric data element. If being added to an array,
      //   the value of this parameter should be *OMIT.
     D  pName                              Const Like(RXS_Var64Kv_t)
     D                                     Options(*Omit:*Varsize)

      // Data to assign to JSON numeric data element. Data should be provided in
      //   a character field or converted by %Char().
      // Example: '1234', '1.76', '-2'
     D  pNumberAsString...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize)

      // RXS_JsonStructureDS_t data structure containing a parent JSON Object or
      //   Json Array.
     D  pStructureDS                       Const LikeDS(RXS_JsonStructureDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create JSON object with number element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a numeric
      * data field to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D itemprice       S              5P 2
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonNumber( 'id' : '7' : RootDS );

        itemprice = 12.50;
        RXS_ComposeJsonNumber( 'price' : %Char(itemprice) : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "id": 7, "price": 12.50 }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple number elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 5 numeric data
      * fields to it, retreives the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonNumber( *OMIT : %Char(i) : RootDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // [1,2,3,4,5]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create JSON object with number element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a numeric
      * data field to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D itemprice       S              5P 2
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonNumber( 'id' : '7' : RootDS );

        itemprice = 12.50;
        RXS_ComposeJsonNumber( 'price' : %Char(itemprice) : RootDS );

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // { "id": 7, "price": 12.50 }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple number elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 5 numeric data
      * fields to it, retreives the created JSON, and then calls 
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonNumber( *OMIT : %Char(i) : RootDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // [1,2,3,4,5]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_ComposeJsonObject()

> Adds a JSON object element to a parent JSON object or array.

This subprocedure is used to add a JSON object element to a parent JSON Object or Array.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeJsonObject...
     D                 PR                  Extproc('RXS_ComposeJsonObject')
     D                                     Opdesc
     D                                     LikeDS(RXS_JsonStructureDS_t)

      // Name to assign to JSON object. If being added to an array, the value of
      //   this parameter should be *OMIT.
     D  pName                              Const Like(RXS_Var64Kv_t)
     D                                     Options(*Omit:*Varsize)

      // RXS_JsonStructureDS_t data structure containing a parent JSON Object or
      //   Json Array.
     D  pStructureDS                       Const LikeDS(RXS_JsonStructureDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create JSON object with object element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a child object
      * to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        ChildDS = RXS_ComposeJsonObject( 'child' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "child": {} }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple object elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 3 child objects 
      * to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 3;
          ChildDS = RXS_ComposeJsonObject( *OMIT : RootDS );
          RXS_ComposeJsonString( 'value' : %Char(i) : ChildDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // [{"value": "1"}, {"value": "2"}, {"value": "3"}]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create JSON object with object element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a child object
      * to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        ChildDS = RXS_ComposeJsonObject( 'child' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "child": {} }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple object elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON array, adds 3 child objects 
      * to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ChildDS         DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ChildDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 3;
          ChildDS = RXS_ComposeJsonObject( *OMIT : RootDS );
          RXS_ComposeJsonString( 'value' : %Char(i) : ChildDS );
        endfor;

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // [{"value": "1"}, {"value": "2"}, {"value": "3"}]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_ComposeJsonString()

> Adds a JSON string element to a parent JSON object or array.

This subprocedure is used to add a JSON string element to a parent JSON Object or Array.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D JSON_ComposeJsonString...
     D                 PR                  Extproc('JSON_ComposeJsonString')
     D                                     Opdesc

      // Name to assign to JSON string data element. If being added to an array,
      //   the value of this parameter should be *OMIT.
     D  pName                              Const Like(RXS_Var64Kv_t)
     D                                     Options(*Omit:*Varsize)

      // Data to assign to JSON string data element.
     D  pInput                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Varsize)

      // RXS_JsonStructureDS_t data structure containing a parent JSON Object or
      //   Json Array.
     D  pStructureDS                       Const LikeDS(RXS_JsonStructureDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create JSON object with string element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a string data
      * field to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonString( 'hello' : 'world' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "hello": "world" }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple string elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds 5 string data
      * fields to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonString( *OMIT : %Char(i) : RootDS );
        endfor;

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // ["1","2","3","4","5"]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create JSON object with string element

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a string data
      * field to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonString( 'hello' : 'world' : RootDS );

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // { "hello": "world" }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create JSON array with multiple string elements

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds 5 string data
      * fields to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );

        for i = 1 to 5;
          RXS_ComposeJsonString( *OMIT : %Char(i) : RootDS );
        endfor;

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // ["1","2","3","4","5"]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_ComposeSection()

> Renders a named section from a loaded template, appending the output to the compose buffer.

This subprocedure writes the named section to the compose engine buffer.

**Note**: This procedure will throw an error if it has been called without the composition engine being initialized via [RXS_StartComposeEngine()](https://isupport.katointegrations.com/rxs/3.3/rxs_startcomposeengine.md).

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeSection...
     D                 PR                  Extproc('RXS_ComposeSection')

      // The name of the section to be written to the template engine buffer.
     D   Section                     50A   Varying
```


## Example Code

### IBM i 6.1+

#### Example 1: Write a Template Section

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates calling RXS_ComposeSection() to write the template
      *  section 'content'. This section would be defined in the XML template EXAMPLE
      *  like so:
      *   ::content
      *   <element>...</element>
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,EXAMPLE

      /free 
       RXS_ComposeSection( content );

       *INLR = *ON;
      /end-free
```


---

# RXS_ComposeVariable()

> Substitutes a named variable placeholder in the current template section with a supplied value.

This searches the currently loaded template for the specified Variable, and replaces it with the Value parameter. If variable is used multiple times, all occurrences will be replaced.

**Note**: This procedure will throw an error if it has been called without the composition engine being initialized via [RXS_StartComposeEngine()](https://isupport.katointegrations.com/rxs/3.3/rxs_startcomposeengine.md).

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ComposeVariable...
     D                 PR                  Extproc('RXS_ComposeVariable')

      // The name of the variable to be replaced in the currently loaded
      //   template.
     D   Variable                    30A   Varying

      // The value which will replace the specified Variable.
     D   Value                             Const Like(RXS_Var16Mv_t)
```


## Example Code

### IBM i 6.1+

#### Example 1: Write a Template Variable

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates calling RXS_ComposeVariable() to assign the 
      *  value of the gFahrenheit field to the template variable 'fahrenheit'. 
      *  This variable would be defined in the XML template EXAMPLE like so:
      *   <fahrenheit>.:fahrenheit:.</fahrenheit>
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,EXAMPLE

     D gFahrenheit     S              3P 1

      /free
       gFahrenheit = 78.6;

       RXS_ComposeVariable( fahrenheit : %Char(gFahrenheit) );

       *INLR = *ON;
      /end-free
```


---

# RXS_Convert()

> Performs common data conversions including Base64/Base64Url encoding and decoding, hex encoding, and CCSID character set conversion.

RXS_Convert is a flexible subprocedure allowing you to perform many commonly required text and data conversions:

- Convert to/from Base64 encoding
- Perform XML entity encoding/decoding to replace characters with the appropriate XML entities:
    - `&` to `&amp;`
    - `"` to `&quot;`
    - `'` to `&apos;`
    - `<` to `&lt;`
    - `>` to `&gt;`
- Encode or decode a URL's querystring
- Convert between CCSIDs (character sets)
- Convert to uppercase or lowercase in a CCSID-aware manner - preferable to the common practice of using the %XLATE built-in function
- General find & replace - you can specify your own characters/strings to perform replacements with. Essentially, this allows developers on IBM i OS 6.1 to access functionality similar to that of the %SCANRPL built-in function added in IBM i OS 7.1.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // The output of the specified conversion operation is returned.
     D RXS_Convert...
     D                 PR                  Extproc('RXS_Convert') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // Field to hold the input of the selected conversion operation. You can
      //   pass a field smaller than 16M without any additional effort.
     D   Input                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Varsize)

      // Holds settings which control how RXS_Convert operates. This parm can
      //   accomodate a number of possible data structures, listed below.
      // Valid values: RXS_ConvertCcsidDS_t, RXS_ConvertCaseDS_t,
      //   RXS_ConvertBase64DS_t, RXS_ConvertXMLEntitiesDS_t,
      //   RXS_ConvertUrlPercentDS_t, RXS_ConvertUserDefinedDS_t
     D   DS                                Like(RXS_Var64K_t)
     D                                     Options(*Varsize)
```


### IBM i 6.1

```rpgle
     D RXS_Convert...
     D                 PR                  Extproc('RXS_Convert') Opdesc

      // Field to hold the output of the selected conversion operation. Note that
      //   if you pass a field smaller than 16M, you will need to specify the
      //   length of the Output in the subfield OutputLength.
     D   Output                            Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize:*Omit)

      // Field to hold the input of the selected conversion operation. You can
      //   pass a field smaller than 16M without any additional effort.
     D   Input                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Varsize:*Omit)

      // Holds settings which control how RXS_Convert operates. This parm can
      //   accomodate a number of possible data structures, listed below.
      // Valid values: RXS_ConvertCcsidDS_t, RXS_ConvertCaseDS_t,
      //   RXS_ConvertBase64DS_t, RXS_ConvertXMLEntitiesDS_t,
      //   RXS_ConvertUrlPercentDS_t, RXS_ConvertUserDefinedDS_t
     D   DS                                Like(RXS_Var64K_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Base64 Encoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to encode data to Base64.
      * Base64 is commonly used to transfer binary data such as images
      * or cryptographic keys/hashes, but may be used on any data.
      * Important to note is that no CCSID conversion is performed on
      * the decoded data. If you are sending text, the receiver will
      * most likely not expect the data to be in EBCDIC, so you may
      * need to use RXS_Convert() to perform CCSID conversion prior
      * to performing Base64 encoding.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gRawData        S                   Like(RXS_Var8Kv_t)
     D gBase64         S                   Like(RXS_Var8Kv_t)

     D gConvBase64DS   DS                  LikeDS(RXS_ConvertBase64DS_t)
      /free
       gRawData = 'hello world!';

       RXS_ResetDS( gConvBase64DS : RXS_DS_TYPE_CONVERTBASE64 );
       gConvBase64DS.EncodeDecode = RXS_ENCODE;
       gBase64 = RXS_Convert( gRawData : gConvBase64DS );

       // After encoding, gBase64 will contain:
       //  aGVsbG8gd29ybGQh

       *INLR = *ON;
      /end-free
```

#### Base64 Decoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to decode Base64 encoded data.
      * Base64 is commonly used to transfer binary data such as images
      * or cryptographic keys/hashes, but may be used on any data.
      * Important to note is that no CCSID conversion is performed on
      * the decoded data. If you are receiving ASCII data that was
      * Base64 encoded, you may need to use RXS_Convert() to convert
      * it to a non-ASCII CCSID.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gBase64         S                   Like(RXS_Var8Kv_t)
     D gDecoded        S                   Like(RXS_Var8Kv_t)

     D gConvBase64DS   DS                  LikeDS(RXS_ConvertBase64DS_t)
      /free
       gBase64 = 'aGVsbG8gd29ybGQh';

       RXS_ResetDS( gConvBase64DS : RXS_DS_TYPE_CONVERTBASE64 );
       gConvBase64DS.EncodeDecode = RXS_DECODE;
       gDecoded = RXS_Convert( gBase64 : gConvBase64DS );

       // After decoding, gDecoded will contain:
       //  hello world!

       *INLR = *ON;
      /end-free
```

#### XML Entity Encoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to encode XML entites in
      * character data. Note that because < and > would need to be
      * encoded, you cannot use RXS_Convert() to encode a complete
      * XML document. Each item being composed into an XML element
      * would need to be individually encoded.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gData            S                  Like(RXS_Var8Kv_t)
     D gEncoded         S                  Like(RXS_Var8Kv_t)

     D gConvXMLEntDS    DS                 LikeDS(RXS_ConvertXMLEntitiesDS_t)
      /free
       gData = 'Bob & Mary';

       RXS_ResetDS( gConvXMLEntDS : RXS_DS_TYPE_CONVERTXMLENTITIES );
       gConvXMLEntDS.EncodeDecode = RXS_ENCODE;
       gEncoded = RXS_Convert( gData : gConvXMLEntDS );

       // After encoding, gEncoded will contain:
       //  Bob &amp; Mary

       *INLR = *ON;
      /end-free
```

#### XML Entity Decoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to decode XML entites back
      * to the corresponding character data. If you have an XML
      * document with encoded characters, it is generally safe to use
      * RXS_Convert() on the entire XML document. One exception to this
      * is when the XML document contains a nested complete XML document
      * which frequently occurs with .Net SOAP services. In that scenario,
      * you need to first parse the inner XML document out, and then
      * perform conversion. Failure to do this may result in an invalid
      * XML document.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gXML             S                  Like(RXS_Var8Kv_t)
     D gDecoded         S                  Like(RXS_Var8Kv_t)

     D gConvXMLEntDS    DS                 LikeDS(RXS_ConvertXMLEntitiesDS_t)
      /free
       gXML = '<xml>Bob &amp; Mary</xml>';

       RXS_ResetDS( gConvXMLEntDS : RXS_DS_TYPE_CONVERTXMLENTITIES );
       gConvXMLEntDS.EncodeDecode = RXS_DECODE;
       gDecoded = RXS_Convert( gXML : gConvXMLEntDS );

       // After decoding, gDecoded will contain:
       //  <xml>Bob & Mary</xml>

       *INLR = *ON;
      /end-free
```

#### URL Percent Encoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to URL-encode data
      * (also known as 'percent encoding'). This is generally used
      * when passing values in a querystring within a URL. Note that
      * it is not possible to perform URL encoding on a complete URL
      * as many of the characters that make up a URL would be
      * erroneously converted. Instead, each value must be encoded
      * and concatenated to the URL.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gUrl            S                   Like(RXS_Var1Kv_t)
     D gRawData        S                   Like(RXS_Var8Kv_t)
     D gEncoded        S                   Like(RXS_Var8Kv_t)

     D gConvUrlDS      DS                  LikeDS(RXS_ConvertUrlPercentDS_t)
      /free
       gURL = 'http://www.example.com?key='

       gRawData = 'hello world!';

       RXS_ResetDS( gConvUrlDS : RXS_DS_TYPE_CONVERTURLPERCENT );
       gConvUrlDS.EncodeDecode = RXS_ENCODE;
       gEncoded = RXS_Convert( gRawData : gConvUrlDS );

       // After encoding, gEncoded will contain:
       //  hello%20world%21

       gURL += gEncoded;

       // gURL now contains:
       //  http://www.example.com?key=hello%20world%21

       *INLR = *ON;
      /end-free
```

#### URL Percent Decoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to decode URL-encoded data
      * (also known as 'percent encoded').
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gEncoded        S                   Like(RXS_Var8Kv_t)
     D gDecoded        S                   Like(RXS_Var8Kv_t)

     D gConvUrlDS      DS                  LikeDS(RXS_ConvertUrlPercentDS_t)
      /free
       gEncoded = 'key%3Dhello%20world%21';

       RXS_ResetDS( gConvUrlDS : RXS_DS_TYPE_CONVERTURLPERCENT );
       gConvUrlDS.EncodeDecode = RXS_DECODE;
       gDecoded = RXS_Convert( gEncoded : gConvUrlDS );

       // After decoding, gDecoded will contain:
       //  key=hello world!

       *INLR = *ON;
      /end-free
```

#### CCSID Conversion

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to perform CCSID conversion to
      * convert from ISO 8859-1 (CCSID 819) to EBCDIC (CCSID 37).
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gData819        S                   Like(RXS_Var8Kv_t)
     D gData37         S                   Like(RXS_Var8Kv_t)

     D gConvCcsidDS    DS                  LikeDS(RXS_ConvertCcsidDS_t)
      /free
       gData819 = x'48656C6C6F20576F726C6421'; 
       RXS_ResetDS( gConvCcsidDS : RXS_DS_TYPE_CONVERTCCSID );
       gConvCcsidDS.From = RXS_CCSID_ISO88591;
       gConvCcsidDS.To = RXS_CCSID_EBCDIC;
       gData37 = RXS_Convert( gData819 : gConvCcsidDS );

       // After conversion, gData37 will contain:
       //  Hello World!

       *INLR = *ON;
      /end-free
```

#### Convert to Uppercase

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to convert lowercase characters
      *  in gData to uppercase. This conversion happens in a CCSID
      *  aware manner. For example, ü will be converted to Ü. This
      *  is recommended over the common practice of using the %Xlate
      *  function with A-Z/a-z constants.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gData           S                   Like(RXS_Var8Kv_t)
     D gUppercase      S                   Like(RXS_Var8Kv_t)

     D gConvCaseDS     DS                  LikeDS(RXS_ConvertCaseDS_t)
      /free
       gData = 'Hello Günter';

       RXS_ResetDS( gConvCaseDS : RXS_DS_TYPE_CONVERTCASE );
       gConvCaseDS.UpperLower = RXS_UPPERCASE;
       gUppercase = RXS_Convert( gData : gConvCaseDS );

       // After conversion, gLowercase will now contain
       //  HELLO GÜNTER

       *INLR = *ON;
      /end-free
```

#### Convert to Lowercase

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to convert uppercase characters
      *  in gData to lowercase. This conversion happens in a CCSID
      *  aware manner. For example, Ü will be converted to ü. This
      *  is recommended over the common practice of using the %Xlate
      *  function with A-Z/a-z constants.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gData           S                   Like(RXS_Var8Kv_t)
     D gLowercase      S                   Like(RXS_Var8Kv_t)

     D gConvCaseDS     DS                  LikeDS(RXS_ConvertCaseDS_t)
      /free
       gData = 'HELLO GÜNTER';

       RXS_ResetDS( gConvCaseDS : RXS_DS_TYPE_CONVERTCASE );
       gConvCaseDS.UpperLower = RXS_LOWERCASE;
       gLowercase = RXS_Convert( gData : gConvCaseDS );

       // After conversion, gLowercase will now contain
       //  hello günter

       *INLR = *ON;
      /end-free
```

#### User Defined Conversion (Scan & Replace)

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to replace instances of
      * specified characters or bytes with the provided replacements.
      * This was originally introduced to provide customers on IBM i
      * 6.1 with something similar to the %ScanRpl built-in function
      * which was only available on IBM i 7.1 or higher. Generally
      * if you are on IBM i 7.1 or higher, you can just use %ScanRpl.
      * The only advantage offered by using RXS_Convert() is that
      * up to 50 replacements may be specified, but each From and To
      * are limited to 32 bytes.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gInput          S                   Like(RXS_Var8Kv_t)
     D gOutput         S                   Like(RXS_Var8Kv_t)

     D gConvUsrDfnDS   DS                  LikeDS(RXS_ConvertUserDefinedDS_t)
      /free
       gInput = 'This is a sample message';

       RXS_ResetDS( gConvUsrDfnDS : RXS_DS_TYPE_CONVERTUSERDEFINED );
       gConvUsrDfnDS.From(1) = 'sample';
       gConvUsrDfnDS.To(1) = 'cool';
       gConvUsrDfnDS.From(2) = 'message';
       gConvUsrDfnDS.To(2) = 'message!';
       gOutput = RXS_Convert( gInput : gConvUsrDfnDS );

       // gOutput now contains:
       //  This is a cool message!

       *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Base64 Encoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to encode data to Base64.
      * Base64 is commonly used to transfer binary data such as images
      * or cryptographic keys/hashes, but may be used on any data.
      * Important to note is that no CCSID conversion is performed on
      * the decoded data. If you are sending text, the receiver will
      * most likely not expect the data to be in EBCDIC, so you may
      * need to use RXS_Convert() to perform CCSID conversion prior
      * to performing Base64 encoding.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gRawData        S                   Like(RXS_Var8Kv_t)
     D gBase64         S                   Like(RXS_Var8Kv_t)

     D gConvBase64DS   DS                  LikeDS(RXS_ConvertBase64DS_t)
      /free
       gRawData = 'hello world!';

       RXS_ResetDS( gConvBase64DS : RXS_DS_TYPE_CONVERTBASE64 );
       gConvBase64DS.EncodeDecode = RXS_ENCODE;
       RXS_Convert( gBase64 : gRawData : gConvBase64DS );

       // After encoding, gBase64 will contain:
       //  aGVsbG8gd29ybGQh

       *INLR = *ON;
      /end-free
```

#### Base64 Decoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to decode Base64 encoded data.
      * Base64 is commonly used to transfer binary data such as images
      * or cryptographic keys/hashes, but may be used on any data.
      * Important to note is that no CCSID conversion is performed on
      * the decoded data. If you are receiving ASCII data that was
      * Base64 encoded, you may need to use RXS_Convert() to convert
      * it to a non-ASCII CCSID.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gBase64         S                   Like(RXS_Var8Kv_t)
     D gDecoded        S                   Like(RXS_Var8Kv_t)

     D gConvBase64DS   DS                  LikeDS(RXS_ConvertBase64DS_t)
      /free
       gBase64 = 'aGVsbG8gd29ybGQh';

       RXS_ResetDS( gConvBase64DS : RXS_DS_TYPE_CONVERTBASE64 );
       gConvBase64DS.EncodeDecode = RXS_DECODE;
       RXS_Convert( gDecoded : gBase64 : gConvBase64DS );

       // After decoding, gDecoded will contain:
       //  hello world!

       *INLR = *ON;
      /end-free
```

#### XML Entity Encoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to encode XML entites in
      * character data. Note that because < and > would need to be
      * encoded, you cannot use RXS_Convert() to encode a complete
      * XML document. Each item being composed into an XML element
      * would need to be individually encoded.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gData            S                  Like(RXS_Var8Kv_t)
     D gEncoded         S                  Like(RXS_Var8Kv_t)

     D gConvXMLEntDS    DS                 LikeDS(RXS_ConvertXMLEntitiesDS_t)
      /free
       gData = 'Bob & Mary';

       RXS_ResetDS( gConvXMLEntDS : RXS_DS_TYPE_CONVERTXMLENTITIES );
       gConvXMLEntDS.EncodeDecode = RXS_ENCODE;
       RXS_Convert( gEncoded : gData : gConvXMLEntDS );

       // After encoding, gEncoded will contain:
       //  Bob &amp; Mary

       *INLR = *ON;
      /end-free
```

#### XML Entity Decoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to decode XML entites back
      * to the corresponding character data. If you have an XML
      * document with encoded characters, it is generally safe to use
      * RXS_Convert() on the entire XML document. One exception to this
      * is when the XML document contains a nested complete XML document
      * which frequently occurs with .Net SOAP services. In that scenario,
      * you need to first parse the inner XML document out, and then
      * perform conversion. Failure to do this may result in an invalid
      * XML document.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gXML             S                  Like(RXS_Var8Kv_t)
     D gDecoded         S                  Like(RXS_Var8Kv_t)

     D gConvXMLEntDS    DS                 LikeDS(RXS_ConvertXMLEntitiesDS_t)
      /free
       gXML = '<xml>Bob &amp; Mary</xml>';

       RXS_ResetDS( gConvXMLEntDS : RXS_DS_TYPE_CONVERTXMLENTITIES );
       gConvXMLEntDS.EncodeDecode = RXS_DECODE;
       RXS_Convert( gDecoded : gXML : gConvXMLEntDS );

       // After decoding, gDecoded will contain:
       //  <xml>Bob & Mary</xml>

       *INLR = *ON;
      /end-free
```

#### URL Percent Encoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to URL-encode data
      * (also known as 'percent encoding'). This is generally used
      * when passing values in a querystring within a URL. Note that
      * it is not possible to perform URL encoding on a complete URL
      * as many of the characters that make up a URL would be
      * erroneously converted. Instead, each value must be encoded
      * and concatenated to the URL.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gUrl            S                   Like(RXS_Var1Kv_t)
     D gRawData        S                   Like(RXS_Var8Kv_t)
     D gEncoded        S                   Like(RXS_Var8Kv_t)

     D gConvUrlDS      DS                  LikeDS(RXS_ConvertUrlPercentDS_t)
      /free
       gURL = 'http://www.example.com?key='

       gRawData = 'hello world!';

       RXS_ResetDS( gConvUrlDS : RXS_DS_TYPE_CONVERTURLPERCENT );
       gConvUrlDS.EncodeDecode = RXS_ENCODE;
       RXS_Convert( gEncoded : gRawData : gConvUrlDS );

       // After encoding, gEncoded will contain:
       //  hello%20world%21

       gURL += gEncoded;

       // gURL now contains:
       //  http://www.example.com?key=hello%20world%21

       *INLR = *ON;
      /end-free
```

#### URL Percent Decoding

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to decode URL-encoded data
      * (also known as 'percent encoded').
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gEncoded        S                   Like(RXS_Var8Kv_t)
     D gDecoded        S                   Like(RXS_Var8Kv_t)

     D gConvUrlDS      DS                  LikeDS(RXS_ConvertUrlPercentDS_t)
      /free
       gEncoded = 'key%3Dhello%20world%21';

       RXS_ResetDS( gConvUrlDS : RXS_DS_TYPE_CONVERTURLPERCENT );
       gConvUrlDS.EncodeDecode = RXS_DECODE;
       RXS_Convert( gDecoded : gEncoded : gConvUrlDS );

       // After decoding, gDecoded will contain:
       //  key=hello world!

       *INLR = *ON;
      /end-free
```

#### CCSID Conversion

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to perform CCSID conversion to
      * convert from ISO 8859-1 (CCSID 819) to EBCDIC (CCSID 37).
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gData819        S                   Like(RXS_Var8Kv_t)
     D gData37         S                   Like(RXS_Var8Kv_t)

     D gConvCcsidDS    DS                  LikeDS(RXS_ConvertCcsidDS_t)
      /free
       gData819 = x'48656C6C6F20576F726C6421'; 
       RXS_ResetDS( gConvCcsidDS : RXS_DS_TYPE_CONVERTCCSID );
       gConvCcsidDS.From = RXS_CCSID_ISO88591;
       gConvCcsidDS.To = RXS_CCSID_EBCDIC;
       RXS_Convert( gData37 : gData819 : gConvCcsidDS );

       // After conversion, gData37 will contain:
       //  Hello World!

       *INLR = *ON;
      /end-free
```

#### Convert to Uppercase

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to convert lowercase characters
      *  in gData to uppercase. This conversion happens in a CCSID
      *  aware manner. For example, ü will be converted to Ü. This
      *  is recommended over the common practice of using the %Xlate
      *  function with A-Z/a-z constants.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gData           S                   Like(RXS_Var8Kv_t)
     D gUppercase      S                   Like(RXS_Var8Kv_t)

     D gConvCaseDS     DS                  LikeDS(RXS_ConvertCaseDS_t)
      /free
       gData = 'Hello Günter';

       RXS_ResetDS( gConvCaseDS : RXS_DS_TYPE_CONVERTCASE );
       gConvCaseDS.UpperLower = RXS_UPPERCASE;
       RXS_Convert( gUppercase : gData : gConvCaseDS );

       // After conversion, gUppercase will now contain
       //  HELLO GÜNTER

       *INLR = *ON;
      /end-free
```

#### Convert to Lowercase

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to convert uppercase characters
      *  in gData to lowercase. This conversion happens in a CCSID
      *  aware manner. For example, Ü will be converted to ü. This
      *  is recommended over the common practice of using the %Xlate
      *  function with A-Z/a-z constants.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gData           S                   Like(RXS_Var8Kv_t)
     D gLowercase      S                   Like(RXS_Var8Kv_t)

     D gConvCaseDS     DS                  LikeDS(RXS_ConvertCaseDS_t)
      /free
       gData = 'HELLO GÜNTER';

       RXS_ResetDS( gConvCaseDS : RXS_DS_TYPE_CONVERTCASE );
       gConvCaseDS.UpperLower = RXS_LOWERCASE;
       RXS_Convert( gLowercase : gData : gConvCaseDS );

       // After conversion, gLowercase will now contain
       //  hello günter

       *INLR = *ON;
      /end-free
```

#### User Defined Conversion (Scan & Replace)

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_Convert() to replace instances of
      * specified characters or bytes with the provided replacements.
      * This was originally introduced to provide customers on IBM i
      * 6.1 with something similar to the %ScanRpl built-in function
      * which was only available on IBM i 7.1 or higher. Generally
      * if you are on IBM i 7.1 or higher, you can just use %ScanRpl.
      * The only advantage offered by using RXS_Convert() is that
      * up to 50 replacements may be specified, but each From and To
      * are limited to 32 bytes.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gInput          S                   Like(RXS_Var8Kv_t)
     D gOutput         S                   Like(RXS_Var8Kv_t)

     D gConvUsrDfnDS   DS                  LikeDS(RXS_ConvertUserDefinedDS_t)
      /free
       gInput = 'This is a sample message';

       RXS_ResetDS( gConvUsrDfnDS : RXS_DS_TYPE_CONVERTUSERDEFINED );
       gConvUsrDfnDS.From(1) = 'sample';
       gConvUsrDfnDS.To(1) = 'cool';
       gConvUsrDfnDS.From(2) = 'message';
       gConvUsrDfnDS.To(2) = 'message!';
       RXS_Convert( gOutput : gInput : gConvUsrDfnDS );

       // gOutput now contains:
       //  This is a cool message!

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_ConvertBase64DS_t

```rpgle
     D RXS_ConvertBase64DS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CONVERTBASE64)

     D   OnErrorMessageType...
     D                                5I 0

      // Set to RXS_ENCODE or RXS_DECODE.
      // Valid values: RXS_ENCODE, RXS_DECODE
      // Default: RXS_DECODE
     D   EncodeDecode                  N

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2048A
```


### RXS_ConvertXmlEntitiesDS_t

```rpgle
     D RXS_ConvertXMLEntitiesDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CONVERTXMLENTITIES)

     D   OnErrorMessageType...
     D                                5I 0

      // Set to RXS_ENCODE or RXS_DECODE.
      // Valid values: RXS_ENCODE, RXS_DECODE
      // Default: RXS_DECODE
     D   EncodeDecode                  N

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2049A
```


### RXS_ConvertUrlPercentDS_t

```rpgle
     D RXS_ConvertUrlPercentDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CONVERTURLPERCENT)

     D   OnErrorMessageType...
     D                                5I 0

      // Set to RXS_ENCODE or RXS_DECODE.
      // Valid values: RXS_ENCODE, RXS_DECODE
      // Default: RXS_DECODE
     D   EncodeDecode                  N

      // Determines whether the returned encoded hexadecimal values are returned
      //   as ASCII bytes (RXS_ASCII) or converted to EBCDIC (RXS_EBCDIC).
      // Valid values: RXS_ASCII, RXS_EBCDIC
      // Default: RXS_ASCII
     D   AsciiEbcdic                   N

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2050A
```


### RXS_ConvertCcsidDS_t

```rpgle
     D RXS_ConvertCcsidDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CONVERTCCSID)

     D   OnErrorMessageType...
     D                                5I 0

      // CCSID to convert the input data from.
     D   From                        10I 0

      // CCSID to convert the input data to.
     D   To                          10I 0

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2048A
```


### RXS_ConvertCaseDS_t

```rpgle
     D RXS_ConvertCaseDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CONVERTCASE)

     D   OnErrorMessageType...
     D                                5I 0

      // The CCSID of the input string. Defaults to the CCSID of the current job.
     D   Ccsid                       10I 0

      // Set to RXS_UPPERCASE or RXS_LOWERCASE.
      // Valid values: RXS_UPPERCASE, RXS_LOWERCASE
      // Default: RXS_UPPERCASE
     D   UpperLower                    N

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2050A
```


### RXS_ConvertUserDefinedDS_t

```rpgle
     D RXS_ConvertUserDefinedDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CONVERTUSERDEFINED)

     D   OnErrorMessageType...
     D                                5I 0

     D   From                        32A   Varying Dim(50)

     D   To                          32A   Varying Dim(50)

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2048A
```



---

# RXS_CreateJson()

> Initializes a JSON document by creating a root object or array to which other JSON elements are attached.

This subprocedure is used to begin JSON composition. It creates a "root" JSON structure that other JSON objects, arrays, and data elements can be attached to.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns an RXS_JsonStructureDS_t which can be used by the RXS JSON
      //   composition subprocedures.
     D RXS_CreateJson...
     D                 PR                  Extproc('RXS_CreateJson') Opdesc
     D                                     LikeDS(RXS_JsonStructureDS_t)

      // RXS_CreateJsonDS_t data structure which controls how RXS_CreateJson()
      //   functions.
     D  pCreateJsonDS...
     D                                     LikeDS(RXS_CreateJsonDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create root JSON object

```rpgle
      *--------------------------------------------------------------
      * This example creates a root JSON structure that is a JSON 
      * object. It allows subsequent JSON objects, arrays, or data
      * elements to be attached in order to build a JSON document.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ItemDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ItemDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );
        // RootDS now contains a JSON structure that looks like this
        //  if printed:
        //
        //  { }

        // Create JSON object named "item" and attach it to RootDS
        ItemDS = RXS_ComposeJsonObject( 'item' : RootDS );
        // JSON now looks like this if printed:
        //
        // { "item": {} }

        // Create JSON number named "id" and attach it to ItemDS
        // Note that RXS_ComposeJsonNumber() uses character data,
        //  not numeric data, to allow it to handle the large
        //  numbers that JSON supports
        RXS_ComposeJsonNumber( 'id' : '7' : ItemDS );
        // JSON now looks like this if printed:
        //
        // { "item": { "id": 7 } }

        // Create JSON string named "name" and attach it to ItemDS
        RXS_ComposeJsonString( 'name' : 'headlight fluid' : ItemDS );
        // JSON now looks like this if printed:
        //
        // { "item": { "id": 7, "name": "headlight fluid" } }


        // Create JSON number named "price" and attach it to ItemDS
        RXS_ComposeJsonNumber( 'price' : '12.50' : ItemDS );

        // Enable JSON "prettification" (format to make more readable)
        //  and retrieve JSON into an RPG character data field
        CreateJsonDS.Prettify = RXS_YES;
        JSON = RXS_GetJsonString( CreateJsonDS );
        // Character data retrieved:
        //
        // {
        //    "item": {
        //      "id": 7,
        //      "name": "headlight fluid",
        //      "price": 12.50
        //    }
        // }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create root JSON array

```rpgle
      *--------------------------------------------------------------
      * This example creates a root JSON structure that is a JSON
      * array. It allows subsequent JSON objects, arrays, or data
      * elements to be attached in order to build a JSON document.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );
        // RootDS now contains a JSON structure that looks like this
        //  if printed:
        //
        //  "[]"

        for i = 1 to 10;
          // Elements in a JSON array do not have a name associated
          //  with them, so the first parameter of the RXS_ComposeJson
          //  subprocedures should be set to *OMIT
          RXS_ComposeJsonNumber( *OMIT : %Char(i) : RootDS );
        endfor;
        // JSON now looks like:
        //
        // [1,2,3,4,5,6,7,8,9,10]

        // Enable JSON "prettification" (format to make more readable)
        //  and retrieve JSON into an RPG character data field
        CreateJsonDS.Prettify = RXS_YES;
        JSON = RXS_GetJsonString( CreateJsonDS );
        // Character data retrieved:
        //
        // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create root JSON object

```rpgle
      *--------------------------------------------------------------
      * This example creates a root JSON structure that is a JSON 
      * object. It allows subsequent JSON objects, arrays, or data
      * elements to be attached in order to build a JSON document.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D ItemDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        RXS_ResetDS( ItemDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );
        // RootDS now contains a JSON structure that looks like this
        //  if printed:
        //
        //  { }

        // Create JSON object named "item" and attach it to RootDS
        ItemDS = RXS_ComposeJsonObject( 'item' : RootDS );
        // JSON now looks like this if printed:
        //
        // { "item": {} }

        // Create JSON number named "id" and attach it to ItemDS
        // Note that RXS_ComposeJsonNumber() uses character data,
        //  not numeric data, to allow it to handle the large
        //  numbers that JSON supports
        RXS_ComposeJsonNumber( 'id' : '7' : ItemDS );
        // JSON now looks like this if printed:
        //
        // { "item": { "id": 7 } }

        // Create JSON string named "name" and attach it to ItemDS
        RXS_ComposeJsonString( 'name' : 'headlight fluid' : ItemDS );
        // JSON now looks like this if printed:
        //
        // { "item": { "id": 7, "name": "headlight fluid" } }


        // Create JSON number named "price" and attach it to ItemDS
        RXS_ComposeJsonNumber( 'price' : '12.50' : ItemDS );

        // Enable JSON "prettification" (format to make more readable)
        //  and retrieve JSON into an RPG character data field
        CreateJsonDS.Prettify = RXS_YES;
        RXS_GetJsonString( JSON : CreateJsonDS );
        // Character data retrieved:
        //
        // {
        //    "item": {
        //      "id": 7,
        //      "name": "headlight fluid",
        //      "price": 12.50
        //    }
        // }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Create root JSON array

```rpgle
      *--------------------------------------------------------------
      * This example creates a root JSON structure that is a JSON
      * array. It allows subsequent JSON objects, arrays, or data
      * elements to be attached in order to build a JSON document.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_ARRAY;
        RootDS = RXS_CreateJson( CreateJsonDS );
        // RootDS now contains a JSON structure that looks like this
        //  if printed:
        //
        //  "[]"

        for i = 1 to 10;
          // Elements in a JSON array do not have a name associated
          //  with them, so the first parameter of the RXS_ComposeJson
          //  subprocedures should be set to *OMIT
          RXS_ComposeJsonNumber( *OMIT : %Char(i) : RootDS );
        endfor;
        // JSON now looks like:
        //
        // [1,2,3,4,5,6,7,8,9,10]

        // Enable JSON "prettification" (format to make more readable)
        //  and retrieve JSON into an RPG character data field
        CreateJsonDS.Prettify = RXS_YES;
        RXS_GetJsonString( JSON : CreateJsonDS );
        // Character data retrieved:
        //
        // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_CreateJsonDS_t

```rpgle
     D RXS_CreateJsonDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CREATEJSON)

     D   OnErrorMessageType...
     D                                5I 0

      // If set to RXS_YES, JSON retrieved with RXS_GetJsonString() will be
      //   formatted with whitespace to be "pretty" and more human readable. If set
      //   to RXS_NO, JSON will be returned in a compact form with as little
      //   whitespace as possible.
     D   Prettify                      N   Inz(RXS_NO)

      // If set to RXS_JSON_STRUCTURE_OBJECT, the JSON document being created
      //   will begin with a root JSON object. If set to RXS_JSON_STRUCTURE_ARRAY,
      //   it will begin with a root JSON array.
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructurePtr...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_Crypt()

> Encrypts or decrypts data using symmetric algorithms with configurable key, IV, and padding options.

This subprocedure performs cryptographic operations like hashing and encryption. Currently supported operations are:

- MD5
- SHA1
- SHA256
- SHA384
- SHA512

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the output of the specified hashing/crytographic operation.
     D RXS_Crypt...
     D                 PR                  Extproc('RXS_Crypt') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // Field to hold the input of the selected hashing/crytographic operation.
      //   You can pass a field smaller than 16M without any additional effort.
     D   Input                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Varsize)

      // Holds settings which control how RXS_Crypt operates. This parm can
      //   accomodate a number of possible data structures, listed below.
     D   DS                                Like(RXS_Var64K_t)
     D                                     Options(*Varsize)
```


### IBM i 6.1

```rpgle
     D RXS_Crypt...
     D                 PR                  Extproc('RXS_Crypt') Opdesc

      // Field to hold the output of the selected hashing/crytographic operation.
      //   Note that if you pass a field smaller than 16M, you will need to specify
      //   the length of the Output in the subfield OutputLength.
     D   Output                            Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize)

      // Field to hold the input of the selected hashing/crytographic operation.
      //   You can pass a field smaller than 16M without any additional effort.
     D   Input                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Varsize)

      // Holds settings which control how RXS_Crypt operates. This parm can
      //   accomodate a number of possible data structures, listed below.
     D   DS                                Like(RXS_Var64K_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1: MD5 Hash of Character Field

```rpgle
      *--------------------------------------------------------------
      * This example code calculates an MD5 hash from a character field.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D HashMD5DS       DS                  LikeDS(RXS_MD5CryptDS_t)
     D                                     Inz(*LikeDS)
     D Data            S                   Like(RXS_Var1Kv_t)
     D Hash            S                   Like(RXS_Var1Kv_t)
      /free
        Data = 'Hello World';

        RXS_ResetDS( HashMD5DS : RXS_DS_TYPE_MD5CRYPT );
        Hash = RXS_Crypt( Data : HashMD5DS );

        RXS_JobLog( 'Hash: %s' : Hash ); // Output: Hash: B10A8DB164E0754105B7A99BE72E3FE5

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: MD5 Hash of Character Field

```rpgle
      *--------------------------------------------------------------
      * This example code calculates an MD5 hash from a character field.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D HashMD5DS       DS                  LikeDS(RXS_MD5CryptDS_t)
     D                                     Inz(*LikeDS)
     D Data            S                   Like(RXS_Var1Kv_t)
     D Hash            S                   Like(RXS_Var1Kv_t)
      /free
        Data = 'Hello World';

        RXS_ResetDS( HashMD5DS : RXS_DS_TYPE_MD5CRYPT );
        RXS_Crypt( Hash : Data : HashMD5DS );

        RXS_JobLog( 'Hash: %s' : Hash ); // Output: Hash: B10A8DB164E0754105B7A99BE72E3FE5

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_MD5CryptDS_t

```rpgle
     D RXS_MD5CryptDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Optional IFS stream file to use as input data for hash operation.
     D   InputStmf                         Like(RXS_Var1Kv_t)

      // CCSID of input data. Used when the input data is of a different CCSID of
      //   the job CCSID. Not necessary if using InputStmf.
     D   InputCcsid                  10I 0 Inz(RXS_CCSID_JOB)

      // CCSID to convert the input data into before hashing. Note that this is
      //   typically going to be RXS_CCSID_ISO88591 or RXS_CCSID_UTF8.
     D   EncryptAsCcsid...
     D                               10I 0 Inz(RXS_CCSID_ISO88591)

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2048A
```


### RXS_SHACryptDS_t

```rpgle
     D RXS_SHACryptDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Determines which SHA algorithm/block size to use when hashing.
      // Valid values: RXS_CRYPT_SHA1_ALGORITHM, RXS_CRYPT_SHA256_ALGORITHM,
      //   RXS_CRYPT_SHA384_ALGORITHM, RXS_CRYPT_SHA512_ALGORITHM
     D   Algorithm                    4P 3

      // Optional IFS stream file to use as input data for hash operation.
     D   InputStmf                         Like(RXS_Var1Kv_t)

      // CCSID of input data. Used when the input data is of a different CCSID of
      //   the job CCSID. Not necessary if using InputStmf.
     D   InputCcsid                  10I 0 Inz(RXS_CCSID_JOB)

      // CCSID to convert the input data into before hashing. Note that this is
      //   typically going to be RXS_CCSID_ISO88591 or RXS_CCSID_UTF8.
     D   EncryptAsCcsid...
     D                               10I 0 Inz(RXS_CCSID_ISO88591)

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  2049A
```



---

# RXS_DestroyJson()

> Frees memory allocated by RXS_CreateJson(); must be called once JSON composition or parsing is complete.

This subprocedure is used to end JSON composition or parsing. It is important to call this subprocedure before your program ends, even if it is ending abnormally.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_DestroyJson...
     D                 PR                  Extproc('RXS_DestroyJson') Opdesc

      // RXS_CreateJsonDS_t data structure or RXS_ParseJsonDS_t data structure.
     D  pJsonDS                            Like(RXS_Var64K_t) Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1: Create and Destroy Simple JSON Object

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a data field 
      * to it, and then calls RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonString( 'hello' : 'world' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Create and Destroy Simple JSON Object

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a data field 
      * to it, and then calls RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonString( 'hello' : 'world' : RootDS );

        RXS_GetJsonString( JSON : CreateJsonDS );

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_CreateJsonDS_t

```rpgle
     D RXS_CreateJsonDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CREATEJSON)

     D   OnErrorMessageType...
     D                                5I 0

      // If set to RXS_YES, JSON retrieved with RXS_GetJsonString() will be
      //   formatted with whitespace to be "pretty" and more human readable. If set
      //   to RXS_NO, JSON will be returned in a compact form with as little
      //   whitespace as possible.
     D   Prettify                      N   Inz(RXS_NO)

      // If set to RXS_JSON_STRUCTURE_OBJECT, the JSON document being created
      //   will begin with a root JSON object. If set to RXS_JSON_STRUCTURE_ARRAY,
      //   it will begin with a root JSON array.
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```


### RXS_JsonStructureDS_t

```rpgle
     D RXS_JsonStructureDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_JSONSTRUCTURE)

     D   OnErrorMessageType...
     D                                5I 0

      // Internal use only
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_GetComposeBuffer()

> Returns the current contents of the compose engine output buffer as a character string.

Retrieves the current composition engine buffer into a variable. The buffer is cleared upon successful retrieval.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the current contents of the compose engine buffer.
     D RXS_GetComposeBuffer...
     D                 PR                  Extproc('RXS_GetComposeBuffer')
     D                                     Opdesc Like(RXS_Var16Mv_t)
     D                                     Rtnparm
```


### IBM i 6.1

```rpgle
     D RXS_GetComposeBuffer...
     D                 PR                  Extproc('RXS_GetComposeBuffer')
     D                                     Opdesc

      // Holds the current contents of the compose engine buffer.
     D   Buffer                            Like(RXS_Var16Mv_t) Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1: Retrieve XML from Buffer

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves the composition engine buffer and 
      *  stores it in the field gXmlRequest. This process might be 
      *  used when calling a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
      /free 
       gXmlRequest = RXS_GetComposeBuffer();

       *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Retrieve XML from Buffer

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves the composition engine buffer and 
      *  stores it in the field gXmlRequest. This process might be 
      *  used when calling a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
      /free 
       RXS_GetComposeBuffer( gXmlRequest );

       *INLR = *ON;
      /end-free
```


---

# RXS_GetEnvVar()

> Retrieves Apache environment variables and HTTP request headers for use in hosted web service programs.

This subprocedure is used to retrieve Apache server's environment variables as well as some incoming HTTP headers. This is typically used in a scenario when you are hosting a web service.

- AUTH_TYPE
- CGI_ASCII_CCSID
- CGI_EBCDIC_CCSID
- CONTENT_LENGTH
- CONTENT_TYPE
- GATEWAY_INTERFACE
- HTTP_ACCEPT
- HTTP_USER_AGENT
- PATH_INFO
- PATH_TRANSLATED
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_HOST
- REMOTE_IDENT
- REQUEST_METHOD
- REMOTE_USER
- SCRIPT_NAME
- SERVER_NAME
- SERVER_PORT
- SERVER_PROTOCOL
- SERVER_SOFTWARE

This subprocedure can also be used to retrieve environment variables set with [RXS_PutEnvVar()](https://isupport.katointegrations.com/rxs/3.3/rxs_putenvvar.md).

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the data retrieved from the environment variable.
     D RXS_GetEnvVar...
     D                 PR                  Extproc('RXS_GetEnvVar') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // Specify the name of the environment variable to retrieve the value of.
     D   EnvVar                      30A   Value
```


### IBM i 6.1

```rpgle
     D RXS_GetEnvVar...
     D                 PR                  Extproc('RXS_GetEnvVar') Opdesc

      // Holds the data retrieved from the environment variable.
     D   Output                            Like(RXS_Var16Mv_t)
     D                                     Options(*varsize)

      // Specify the name of the environment variable to retrieve the value of.
     D   EnvVar                      30A   Value
```


## Example Code

### IBM i 7.1+

#### Example 1: Retrieving an Environment Variable

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves an environment variable. 
      * The environment variable being retrieved, REQUEST_METHOD, would 
      * be set by the Apache server, which means that it can only be 
      * retrieved if this program is used as a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D Data            S                   Like(RXS_Var1Kv_t)

      /free
       Data = RXS_GetEnvVar( 'REQUEST_METHOD' ); 
       RXS_JobLog( 'REQUEST METHOD? %s' : Data );
       *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Retrieving an Environment Variable

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves an environment variable. 
      * The environment variable being retrieved, REQUEST_METHOD, would 
      * be set by the Apache server, which means that it can only be 
      * retrieved if this program is used as a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D Data            S                   Like(RXS_Var1Kv_t)

      /free
       RXS_GetEnvVar( Data : 'REQUEST_METHOD' ); 
       RXS_JobLog( 'REQUEST METHOD? %s' : Data );
       *INLR = *ON;
      /end-free
```


---

# RXS_GetJobCcsid()

> Returns the CCSID of the current job.

This subprocedure retrieves the CCSID of the current job. This may be used in character data conversion, such as with [RXS_Convert()](https://isupport.katointegrations.com/rxs/3.3/rxs_convert.md).

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns the CCSID of the current job.
     D RXS_GetJobCcsid...
     D                 PR            10I 0 Extproc('RXS_GetJobCcsid') Opdesc
```


## Example Code

### IBM i 6.1+

#### Example 1: Job CCSID Retrieval

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves the CCSID, and then outputs it to
      * the job log using RXS_JobLog();
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D CCSID           S             10I 0
      /free
        CCSID = RXS_GetJobCCSID();

        RXS_JobLog( 'Job CCSID: %s' : %Char(CCSID) );

        *INLR = *ON;
      /end-free
```


---

# RXS_GetJsonString()

> Returns the composed JSON document as a character string.

This subprocedure retrieves JSON composed by [RXS_CreateJson()](https://isupport.katointegrations.com/rxs/3.3/rxs_createjson.md) and the other JSON composition subprocedures.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the contents of the created JSON structure.
     D RXS_GetJsonString...
     D                 PR                  Extproc('RXS_GetJsonString')
     D                                     Opdesc Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // RXS_CreateJsonDS_t data structure which controls how RXS_CreateJson()
      //   functions.
     D  pJsonCreateDS...
     D                                     Const LikeDS(RXS_JsonCreateDS_t)
```


### IBM i 6.1

```rpgle
     D RXS_GetJsonString...
     D                 PR                  Extproc('RXS_GetJsonString')
     D                                     Opdesc

      // Holds the contents of the created JSON structure.
     D  pOutput                            Like(RXS_Var16Mv_t) Options(*Varsize)

      // RXS_CreateJsonDS_t data structure which controls how RXS_CreateJson()
      //   functions.
     D  pJsonCreateDS...
     D                                     Const LikeDS(RXS_JsonCreateDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Retrieve JSON from JSON structure

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a string data
      * field to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonString( 'hello' : 'world' : RootDS );

        JSON = RXS_GetJsonString( CreateJsonDS );
        // JSON looks like:
        //
        // { "hello": "world" }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Retrieve JSON from JSON structure

```rpgle
      *--------------------------------------------------------------
      * This example creates a simple JSON object, adds a string data
      * field to it, retrieves the created JSON, and then calls
      * RXS_DestroyJson() to clean up.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JSON            S                   Like(RXS_Var64Kv_t)

     D CreateJsonDS    DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
      /free
        RXS_ResetDS( CreateJsonDS : RXS_DS_TYPE_CREATEJSON );
        RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
        
        CreateJsonDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
        RootDS = RXS_CreateJson( CreateJsonDS );

        RXS_ComposeJsonString( 'hello' : 'world' : RootDS );

        RXS_GetJsonString( JSON : CreateJsonDS );
        // JSON looks like:
        //
        // { "hello": "world" }

        RXS_DestroyJson( CreateJsonDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_CreateJsonDS_t

```rpgle
     D RXS_CreateJsonDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CREATEJSON)

     D   OnErrorMessageType...
     D                                5I 0

      // If set to RXS_YES, JSON retrieved with RXS_GetJsonString() will be
      //   formatted with whitespace to be "pretty" and more human readable. If set
      //   to RXS_NO, JSON will be returned in a compact form with as little
      //   whitespace as possible.
     D   Prettify                      N   Inz(RXS_NO)

      // If set to RXS_JSON_STRUCTURE_OBJECT, the JSON document being created
      //   will begin with a root JSON object. If set to RXS_JSON_STRUCTURE_ARRAY,
      //   it will begin with a root JSON array.
     D   JsonStructureType...
     D                                 N   Inz(RXS_JSON_STRUCTURE_OBJECT)

      // Internal use only
     D   JsonStructure@...
     D                                 *   Inz(*Null)

      // Specifies the CCSID of the data being passed into the JSON composition
      //   subprocedures. Default is job CCSID.
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID that the JSON will be output as from
      //   RXS_GetJsonString(). Default is job CCSID.
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)
```



---

# RXS_GetStdIn()

> Reads the HTTP request body (stdin) into an RPG variable; used to receive POST data in hosted web service programs.

This subprocedure is used to read data sent to you via HTTP POST into a field up to 16MB, or into an IFS STMF file.

This is typically used when you are offering a web service.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the data retrieved from STDIN unless a STMF was specified in a
      //   RXS_GetStdInDS_t data structure passed in the DS parameter.
     D RXS_GetStdIn...
     D                 PR                  Extproc('RXS_GetStdIn') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // Optional RXS_GetStdInDS_t data structure used to convert received data
      //   to a specified CCSID.
     D   DS                                Likeds(RXS_GetStdInDS_t)
     D                                     Options(*Varsize:*Nopass)
```


### IBM i 6.1

```rpgle
     D RXS_GetStdIn...
     D                 PR                  Extproc('RXS_GetStdIn') Opdesc

      // Holds the data retrieved from STDIN.
     D   Output                            Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize)

      // Optional RXS_GetStdInDS_t data structure used to convert received data
      //   to a specified CCSID.
     D   DS                                Likeds(RXS_GetStdInDS_t)
     D                                     Options(*Varsize:*Nopass)
```


## Example Code

### IBM i 7.1+

#### Example 1: Read STDIN To Character Field

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves data from standard in and stores it
      *  in the field gXmlRequest. 
      * This process might be used when offering a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
      /free
       gXmlRequest = RXS_GetStdIn();

       *INLR = *ON;
      /end-free
```

#### Example 2: Read STDIN To IFS STMF

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves data from STDIN and write it 
      *  to the specified IFS file.
      * This process might be used when offering a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D GetStdInDS      DS                  LikeDS(RXS_GetStdInDS_t)
      /free
       RXS_ResetDS( GetStdInDS : RXS_DS_TYPE_GETSTDIN ); 
       GetStdInDS.Stmf = '/tmp/over16mb.xml';
       RXS_GetStdIn(GetStdInDS);

       *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Read STDIN To Character Field

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves data from standard in and stores it
      *  in the field gXmlRequest. 
      * This process might be used when offering a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
      /free
       RXS_GetStdIn( gXmlRequest );

       *INLR = *ON;
      /end-free
```

#### Example 2: Read STDIN To IFS STMF

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves data from STDIN and write it 
      *  to the specified IFS file.
      * This process might be used when offering a web service.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D GetStdInDS      DS                  LikeDS(RXS_GetStdInDS_t)
      /free
       RXS_ResetDS( GetStdInDS : RXS_DS_TYPE_GETSTDIN ); 
       GetStdInDS.Stmf = '/tmp/over16mb.xml';
       RXS_GetStdIn(GetStdInDS);

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_GetStdInDS_t

```rpgle
     D RXS_GetStdInDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_GETSTDIN)

     D   OnErrorMessageType...
     D                                5I 0

      // Specifies the CCSID to convert the inbound data into.
     D   Ccsid                       10I 0

      // Specifies the STMF to load with the contents of STDIN.
     D   Stmf                              Like(RXS_Var1Kv_t)
```



---

# RXS_GetStmf()

> Reads the contents of an IFS stream file into an RPG variable.

This subprocedure retrieves up to 16MB of data from a specified IFS stream file.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns data retrieved from the specified IFS stream file.
     D RXS_GetStmf...
     D                 PR                  Extproc('RXS_GetStmf') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // RXS_GetStmfDS_t data structure used to configure the RXS_GetStmf() call
      //   and specify the IFS stream file to read.
     D   DS                                Likeds(RXS_GetStmfDS_t)
```


### IBM i 6.1

```rpgle
     D RXS_GetStmf...
     D                 PR                  Extproc('RXS_GetStmf')

      // Holds the data retrieved from the specified IFS stream file.
     D   Output                            Like(RXS_Var16Mv_t)
     D                                     Options(*Omit:*Varsize)

      // RXS_GetStmfDS_t data structure used to configure the RXS_GetStmf() call
      //   and specify the IFS stream file to read.
     D   DS                                Likeds(RXS_GetStmfDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1: Read Character String from IFS stream file

```rpgle
      *--------------------------------------------------------------
      * This example code reads the contents of the file 
      * /tmp/rxs_getstmf.txt into the Data field.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D GetStmfDS       DS                  LikeDS(RXS_GetStmfDS_t)
     D Data            S                   Like(RXS_Var64Kv_t)
      /free

       RXS_ResetDS( GetStmfDS : RXS_DS_TYPE_GETSTMF );
       GetStmfDS.Stmf = '/tmp/rxs_getstmf.txt';
       Data = RXS_GetStmf( GetStmfDS );
       *INLR = *ON;

      /end-free
```

#### Example 2: Read Chunks of Characters from an IFS stream file while looping

```rpgle
      *--------------------------------------------------------------
      * This example code reads the contents of the file 
      * /tmp/rxs_getstmf.txt and outputs the entire file to STDOUT, 
      * even if the length of the file is greater than the Data variable.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D GetStmfDS       DS                  LikeDS(RXS_GetStmfDS_t)
     D Data            S                   Like(RXS_Var64Kv_t)
      /free

       RXS_ResetDS( GetStmfDS : RXS_DS_TYPE_GETSTMF );
       GetStmfDS.Stmf = '/tmp/rxs_getstmf.txt';
       GetStmfDS.ChunkedLength = %size(Data - 4);
       Dou %len(Data) = 0;
         Data = RXS_GetStmf( GetStmfDS );
         RXS_PutStdOut( Data );
       Enddo;
       *INLR = *ON;

      /end-free
```

### IBM i 6.1

#### Example 1: Read Character String from IFS stream file

```rpgle
      *--------------------------------------------------------------
      * This example code reads the contents of the file 
      * /tmp/rxs_getstmf.txt into the Data field.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D GetStmfDS       DS                  LikeDS(RXS_GetStmfDS_t)
     D Data            S                   Like(RXS_Var64Kv_t)
      /free

       RXS_ResetDS( GetStmfDS : RXS_DS_TYPE_GETSTMF );
       GetStmfDS.Stmf = '/tmp/rxs_getstmf.txt';
       RXS_GetStmf( Data : GetStmfDS );
       *INLR = *ON;

      /end-free
```

#### Example 2: Read Chunks of Characters from an IFS stream file while looping

```rpgle
      *--------------------------------------------------------------
      * This example code reads the contents of the file 
      * /tmp/rxs_getstmf.txt and outputs the entire file to STDOUT, 
      * even if the length of the file is greater than the Data variable.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D GetStmfDS       DS                  LikeDS(RXS_GetStmfDS_t)
     D Data            S                   Like(RXS_Var64Kv_t)
      /free

       RXS_ResetDS( GetStmfDS : RXS_DS_TYPE_GETSTMF );
       GetStmfDS.Stmf = '/tmp/rxs_getstmf.txt';
       GetStmfDS.ChunkedLength = %size(Data - 4);
       Dou %len(Data) = 0;
         RXS_GetStmf( Data : GetStmfDS );
         RXS_PutStdOut( Data );
       Enddo;  
       *INLR = *ON;

      /end-free
```

## Data Structures

### RXS_GetStmfDS_t

```rpgle
     D RXS_GetStmfDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_GETSTMF)

     D   OnErrorMessageType...
     D                                5I 0

      // Specifies IFS path to a stream file to retrieve.
     D   Stmf                              Like(RXS_Var1Kv_t)

      // The CCSID to convert the data read from the IFS stream file into.
     D   ToCcsid                     10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *   Inz(*Null)

      // Internal use only
     D   OutputLength                10I 0

      // This is a returned value containing the number of bytes stored in the
      //   stream file.
     D   StmfSize                    10I 0

      // The length to use when "chunks" of the stream file are to be returned.
      //   See example 2.
     D   ChunkedLength...
     D                               10I 0

      // Internal use only
     D   ChunkedOffset...
     D                              128A
```



---

# RXS_GetUrlVar()

> Extracts a named variable from the URL query string; used to read GET parameters in hosted web service programs.

This subprocedure is used to retrieve variables from the URL that was used to call the current program as a web service.

Variables on a URL are specified in name-value pairs. For example:

`http://mysite.com/rxs/mywebservice?customer=1&code=3`

In this example, there are two variables: **customer** and **code**, and each of them has a value of 1 and 3 respectively.

**Note:** This API is only meant to be used in a program acting as a webservice. Using it in other programs will throw errors.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the data retrieved from the URL variable.
     D RXS_GetUrlVar...
     D                 PR                  Extproc('RXS_GetUrlVar') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // Specify the name of the URL variable which is to be retrieved.
     D   UrlVar                     300A   Value

     D   Input                             Like(RXS_Var16Mv_t)
     D                                     Options(*Nopass:*Varsize)
```


### IBM i 6.1

```rpgle
     D RXS_GetUrlVar...
     D                 PR                  Extproc('RXS_GetUrlVar') Opdesc

      // Holds the data retrieved from the URL variable.
     D   Output                            Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize)

      // Specify the name of the URL variable to retrieve the value of.
     D   UrlVar                     300A   Value

     D   Input                             Like(RXS_Var16Mv_t)
     D                                     Options(*Nopass:*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1: Retrieve Values from the Querystring of a URL

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves variable values from the URL that 
      * was used to call this program.
      * For example, if this program were called with the following URL:
      * http://mysite.com/rxs/mywebservice?customer=1&code=3
      * The output in the job log would be:
      * Customer: 1
      * Code: 3
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D VarCustomer     S                   Like(RXS_Var1Kv_t)
     D VarCode         S                   Like(RXS_Var1Kv_t)

      /free

       VarCustomer = RXS_GetUrlVar( 'customer' );

       VarCode = RXS_GetUrlVar( 'code' );

       RXS_JobLog( 'Customer: %s' : VarCustomer ); 
       RXS_JobLog( 'Code: %s' : VarCode ); 

       *INLR = *ON;
      /end-free
```

#### Example 2: Retrieve Values from the Path of a URL

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves variable values from the URL that 
      * was used to call this program.
      * For example, if this program were called with the following URL:
      * http://mysite.com/rxs/mywebservice/customer/15/code/43
      * The output in the job log would be:
      * Customer: 5
      * Code: 43
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D VarCustomer     S                   Like(RXS_Var1Kv_t)
     D VarCode         S                   Like(RXS_Var1Kv_t)

      /free

       VarCustomer = RXS_GetUrlVar( 'customer/' );

       VarCode = RXS_GetUrlVar( 'code/' );

       RXS_JobLog( 'Customer: %s' : VarCustomer );
       RXS_JobLog( 'Code: %s' : VarCode );

       *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1: Retrieve Values from the Querystring of a URL

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves variable values from the URL that 
      * was used to call this program.
      * For example, if this program were called with the following URL:
      * http://mysite.com/rxs/mywebservice?customer=1&code=3
      * The output in the job log would be:
      * Customer: 1
      * Code: 3
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D VarCustomer     S                   Like(RXS_Var1Kv_t)
     D VarCode         S                   Like(RXS_Var1Kv_t)

      /free

       RXS_GetUrlVar( VarCustomer : 'customer' );

       RXS_GetUrlVar( VarCode : 'code' );

       RXS_JobLog( 'Customer: %s' : VarCustomer ); 
       RXS_JobLog( 'Code: %s' : VarCode ); 

       *INLR = *ON;
      /end-free
```

#### Example 2: Retrieve Values from the Path of a URL

```rpgle
      *--------------------------------------------------------------
      * This example code retrieves variable values from the URL that 
      * was used to call this program.
      * For example, if this program were called with the following URL:
      * http://mysite.com/rxs/mywebservice/customer/15/code/43
      * The output in the job log would be:
      * Customer: 5
      * Code: 43
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D VarCustomer     S                   Like(RXS_Var1Kv_t)
     D VarCode         S                   Like(RXS_Var1Kv_t)

      /free

       RXS_GetUrlVar( VarCustomer : 'customer/' );

       RXS_GetUrlVar( VarCode : 'code/' );

       RXS_JobLog( 'Customer: %s' : VarCustomer );
       RXS_JobLog( 'Code: %s' : VarCode );

       *INLR = *ON;
      /end-free
```


---

# RXS_JobLog()

> Writes a message to the job log.

This subprocedure is used to easily output messages to the job log. A single character field can be passed in to output in a direct and unmodified fashion, or the API can be called with multiple character data parameters and utilize character replacement.

To perform character replacement, you'll need to include one or more placeholders, indicated by the following string: `%s`

Each Replacement# parameter will replace the corresponding `%s`. For example, Replacement1 will replace the first `%s`, while Replacement6 will replace the sixth `%s` present in the string.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_JobLog...
     D                 PR                  Extproc('RXS_JobLog')
     D                                     Opdesc

      // Simple character data message to be printed to the job log, or a
      //   formatted character data string to use with replacements.
     D   Output                            Like(RXS_Var32Kv_t) Const
     D                                     Options(*Varsize)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement1...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement2...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement3...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement4...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement5...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement6...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement7...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement8...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement9...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)

      // Optional: Used as a replacement in the formatted character data string
      //   passed in Output.
     D   Replacement10...
     D                                     Like(RXS_Var1Kv_t) Const
     D                                     Options(*Varsize: *Nopass)
```


## Example Code

### IBM i 6.1+

#### Example 1: Write to Job Log Without Placeholders

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_JobLog() in the most simple form to just
      * write a basic character string to the job log.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JobLogMsg       S                   Like(RXS_Var1Kv_t)
      /free
      
        JobLogMsg = 'Hello world!';
        RXS_JobLog( JobLogMsg );

        //Job Log Output: Hello world!

        *INLR = *ON;
      /end-free
```

#### Example 2: Write to Job Log With a Single Placeholder

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_JobLog() with a string which contains a
      * single replacement placeholder.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JobLogMsg       S                   Like(RXS_Var1Kv_t)
      /free

        JobLogMsg = 'Hello %s!';
        RXS_JobLog( JobLogMsg : 'Bob');

        //Job Log Output: Hello Bob!

        *INLR = *ON;
      /end-free
```

#### Example 3: Write to Job Log With Multiple Placeholders

```rpgle
      *--------------------------------------------------------------
      * This example uses RXS_JobLog() with a string which contains
      * multiple replacement placeholders.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JobLogMsg       S                   Like(RXS_Var1Kv_t)
      /free

        JobLogMsg = 'Hello %s! Your lucky number is: %s';
        RXS_JobLog( JobLogMsg : 'Bob' : %Char(7) );

        //Job Log Output: Hello Bob! Your lucky number is: 7

        *INLR = *ON;
      /end-free
```


---

# RXS_OpenDom()

> Initializes DOM-based XML parsing from a character field or IFS stream file, enabling XPath-based node navigation.

This subprocedure initializes DOM parsing for a specified chunk of XML.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns an RXS_ParseDOMDS_t which contains information and pointers used
      //   by subsequent DOM parsing APIs.
     D RXS_OpenDom     PR                  Extproc('RXS_OpenDom') Opdesc
     D                                     Like(RXS_ParseDOMDS_t)

      // Chunk of XML to be used for subsequent DOM operations.
     D  Xml                                Like(RXS_Var16Mv_t) Const
     D                                     Options(*Omit:*Varsize)

      // Holds RXS_OpenDomDS_t used to set up initial DOM parsing options.
     D  DS                                 LikeDS(RXS_OpenDomDS_t)
     D                                     Options(*Nopass:*Varsize)
```


## Example Code

### IBM i 6.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates configuring DOM parsing using a RXS_ParseDOMDS_t
      *  datastructure.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D OpenDomDS       DS                  LikeDS(RXS_OpenDOMDS_t)
     D RootDomDS       DS                  LikeDS(RXS_ParseDOMDS_t)
      /free
       RXS_ResetDS( OpenDomDs : RXS_DS_TYPE_OPENDOM );
       RXS_ResetDS( RootDomDs : RXS_DS_TYPE_PARSEDOM );
       OpenDomDs.InputCcsid = RXS_CCSID_UTF8;                               
       RootDomDS = RXS_OpenDom( gXmlResponse : OpenDomDS );

       gXPath = RXS_XPath( '/*:Envelope/*:Body/*:FahrenheitToCelsius' +     
         'Response/*:FahrenheitToCelsiusResult' );
       
       gCelsius = %Dec( RXS_ParseDomToText( gXPath : RootDomDS ) : 11 : 7 );

       RXS_CloseDom( RootDomDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_OpenDomDS_t

```rpgle
     D RXS_OpenDomDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specifies the CCSID of the XML being parsed.
     D   InputCcsid...
     D                               10I 0

      // Specifies the CCSID the parsed data will be converted to.
     D   OutputCcsid...
     D                               10I 0

      // Specifies an IFS path to an XML file to parse instead of the Input parm.
     D   Stmf...
     D                                     Like(RXS_Var1Kv_t)
```



---

# RXS_Parse()

> Event-based XML parser that calls a handler subprocedure for element begin, content, end, and attribute events.

This subprocedure is used to set up XML parsing. Use of this subprocedure requires an XML handler subprocedure to also be written.

The events recognized by the XML parser are:

| Event | Format | Example |
| ----- |-| ------ |
| Element Begin | `>` | `Request>` |
| Element Content | `/` | `Request/Item/` |
| Element End | `/>` | `Request/>` |
| Attribute | `@` | `Request/Item@Attribute` |

The Element Content and Attribute events will return data, which can be retrieved with [RXS_STR()](https://isupport.katointegrations.com/rxs/3.3/rxs_str.md).

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_Parse...
     D                 PR                  Extproc('RXS_Parse')

      // Holds the XML data to be passed to the parsing subprocedure(s). Will be
      //   ignored if the Stmf subfield of the DS parameter is set.
     D   Input                             Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize)

      // Holds a RXS_ParseDS_t data structure which controls how RXS_Parse()
      //   functions.
     D   ParseDS                           Likeds(RXS_ParseDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Simple Parse

```rpgle
      *--------------------------------------------------------------
      * This example parses a simple XML structure stored in the field
      * 'XML' and stores the data contained in nodes <Node1> and <Node2>
      * in the corresponding global fields.
      *
      * At a high level, the XML is passed into the XmlHandler() 
      * subprocedure in small chunks. Then, a SELECT block is used to
      * detect whether or not the current chunk of XML matches one
      * you'd like to handle. If it does, you can extract the data from
      * an element of attribute using RXS_STR().
      *
      * Once the XML has been entirely passed through XmlHander(), control
      * returns to the main part of the program.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D XmlHandler      PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D ParseDS         DS                  LikeDS(RXS_ParseDS_t)
     D                                     Inz(*LikeDS)
     D XML             S                   Like(RXS_Var64Kv_t)
     D Node1           S                   Like(RXS_Var1Kv_t)
     D Node2           S                   Like(RXS_Var1Kv_t)
      /free
        XML = '<?xml version="1.0" encoding="utf-8"?>' +
         '<Root><Node1>Hello</Node1><Node2>World</Node2></Root>';

        RXS_ResetDS( ParseDS : RXS_DS_TYPE_PARSE );
        ParseDS.GlobalHandler = %Paddr( XmlHandler );
        RXS_Parse( XML : ParseDS );

        RXS_JobLog( 'Node 1: %s' : Node1 );
        RXS_JobLog( 'Node 2: %s' : Node2 );

        *INLR = *ON;
      /end-free

     P XmlHandler      B
     D XmlHandler      PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value
      /free
        select;
          when pXPath = '/Root/Node1/';
            Node1 = RXS_STR( pData : pDataLen );
          when pXPath = '/Root/Node2/';
            Node2 = RXS_STR( pData : pDataLen );
        endsl;
      /end-free
     P                 E
```

### IBM i 6.1

#### Example 1: Simple Parse

```rpgle
      *--------------------------------------------------------------
      * This example parses a simple XML structure stored in the field
      * 'XML' and stores the data contained in nodes <Node1> and <Node2>
      * in the corresponding global fields.
      *
      * At a high level, the XML is passed into the XmlHandler() 
      * subprocedure in small chunks. Then, a SELECT block is used to
      * detect whether or not the current chunk of XML matches one
      * you'd like to handle. If it does, you can extract the data from
      * an element of attribute using RXS_STR().
      *
      * Once the XML has been entirely passed through XmlHander(), control
      * returns to the main part of the program.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D XmlHandler      PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D ParseDS         DS                  LikeDS(RXS_ParseDS_t)
     D                                     Inz(*LikeDS)
     D XML             S                   Like(RXS_Var64Kv_t)
     D Node1           S                   Like(RXS_Var1Kv_t)
     D Node2           S                   Like(RXS_Var1Kv_t)
      /free
        XML = '<?xml version="1.0" encoding="utf-8"?>' +
         '<root><node1>Hello</node1><node2>World</node2></root>';

        RXS_ResetDS( ParseDS : RXS_DS_TYPE_PARSE );
        ParseDS.GlobalHandler = %Paddr( XmlHandler );
        RXS_Parse( XML : ParseDS );

        RXS_JobLog( 'Node 1: %s' : Node1 );
        RXS_JobLog( 'Node 2: %s' : Node2 );

        *INLR = *ON;
      /end-free

     P XmlHandler      B
     D XmlHandler      PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value
      /free
        select;
          when pXPath = '/root/node1/';
            RXS_STR( Node1 : pData : pDataLen );
          when pXPath = '/root/node2/';
            RXS_STR( Node2 : pData : pDataLen );
        endsl;
      /end-free
     P                 E
```

## Data Structures

### RXS_ParseDS_t

```rpgle
     D RXS_ParseDS_t   DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     Like(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Holds a PROCPTR to an XmlHandler subprocedure used to handle all
      //   possible XML events.
     D   GlobalHandler...
     D                                 *   Procptr

      // Holds a PROCPTR to an XmlHandler subprocedure which will only respond to
      //   XML content events.
     D   ElementContentHandler...
     D                                 *   Procptr

      // Holds a PROCPTR to an XmlHandler subprocedure which will only respond to
      //   XML begin events.
     D   ElementBeginHandler...
     D                                 *   Procptr

      // Holds a PROCPTR to an XmlHandler subprocedure which will only respond to
      //   XML end events.
     D   ElementEndHandler...
     D                                 *   Procptr

      // Holds a PROCPTR to an XmlHandler subprocedure which will only respond to
      //   XML attribute events.
     D   AttributeHandler...
     D                                 *   Procptr

      // Controls whether namespaces are factored into XML parsing - if RXS_NO,
      //   they will be ignored.
     D   IncludeNamespaces...
     D                                 N

      // Specifies the CCSID of the XML being parsed.
     D   InputCcsid...
     D                               10I 0

      // Specifies the CCSID the parsed data will be converted to.
     D   OutputCcsid...
     D                               10I 0

      // Specifies an IFS path to an XML file to parse instead of the Input parm.
     D   Stmf...
     D                                     Like(RXS_Var1Kv_t)
```



---

# RXS_ParseDomToDom()

> Navigates the DOM to a node matching an XPath expression and returns a child DOM handle for further traversal.

This subprocedure retrieves a new RXS_ParseDomDS_t data structure from a parent XML structure, allowing you to retrieve and work with a subset of a larger XML document.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns an RXS_ParseDomDS_t which contains information and pointers used
      //   by subsequent DOM parsing APIs.
     D RXS_ParseDomToDom...
     D                 PR                  Extproc('RXS_ParseDomToDom') Opdesc
     D                                     Like(RXS_ParseDomDS_t)

      // XPath used to determine which nodes are retrieved into the
      //   RXS_ParseDomDS_t return data structure.
     D  XPath                              Like(RXS_Var8Kv_t) Const
     D                                     Options(*Varsize)

      // RXS_ParseDomDS_t data structure, e.g. the "parent" structure.
     D  DS                                 Like(RXS_ParseDomDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates retrieving a DOM from a specified XPath 
      *  and storing it in a RXS_ParseDomDS_t datastructure for further 
      *  processing.
      * This example uses IBM i 7.1+ prototypes for most calls; refer to
      *  the individual documentation for these subprocedures for the 
      *  6.1 prototypes.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gRootDomDS      DS                  LikeDS(RXS_OpenDomDS_t)
     D gBookDomDS      DS                  LikeDS(RXS_ParseDomDS_t)

     D gXml            S                   Like(RXS_Var8Kv_t)
     D gXPath          S                   Like(RXS_Var1Kv_t)
      /free
       RXS_ResetDS( gRootDomDS : RXS_DS_TYPE_OPENDOM );
       RXS_ResetDS( gBookDomDS : RXS_DS_TYPE_PARSEDOM );

       gXml = RXS_GetStdIn();
       gRootDomDS = RXS_OpenDom( gXml );

       gXPath = RXS_XPath( '/*:bookstore/*:book' );
       gBookDomDS = RXS_ParseDomToDom( gXPath : gRootDomDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_ParseDomDS_t

```rpgle
     D RXS_ParseDomDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specifies the CCSID of the XML being parsed.
     D   InputCcsid...
     D                               10I 0

      // Specifies the CCSID the parsed data will be converted to.
     D   OutputCcsid...
     D                               10I 0

      // Specifies an IFS path to an XML file to parse instead of the Input parm.
     D   Stmf...
     D                                     Like(RXS_Var1Kv_t)

      // Contains the current count of XML nodes tracked by this data structure.
     D   NodeCount                   10U 0

     D   NodeType                    10I 0

      // Internal use only
     D   xmlPtr                        *

      // Internal use only
     D   docPtr                        *

      // Internal use only
     D   contextPtr                    *

      // Internal use only
     D   dictPtr                       *

      // Internal use only
     D   currentPtr                    *

      // Internal use only
     D   parentPtr                     *

      // Internal use only
     D   Reserved                  4096A
```



---

# RXS_ParseDomToText()

> Extracts the text content of an XML node matching an XPath expression.

This subprocedure retrieves the text result of a specific XPath.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the text retrieved from the specified XPath.
     D RXS_ParseDomToText...
     D                 PR                  Extproc('RXS_ParseDomToText') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // XPath used to determine which nodes are retrieved into the
      //   RXS_ParseDomDS_t return data structure.
     D  XPath                              Like(RXS_Var8Kv_t) Const
     D                                     Options(*Varsize)

      // RXS_ParseDomDS_t data structure, e.g. the "parent" structure.
     D  DS                                 Like(RXS_ParseDomDS_t)
     D                                     Options(*Varsize)
```


### IBM i 6.1

```rpgle
     D RXS_ParseDomToText...
     D                 PR                  Extproc('RXS_ParseDomToText') Opdesc

      // Holds the text retrieved from the specified XPath.
     D  Output                             Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize)

      // XPath used to determine which nodes are retrieved into the
      //   RXS_ParseDomDS_t return data structure.
     D  XPath                              Like(RXS_Var8Kv_t) Const
     D                                     Options(*Varsize)

      // RXS_ParseDomDS_t data structure, e.g. the "parent" structure.
     D  DS                                 Like(RXS_ParseDomDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates retrieving a text value from a specified  
      *  XPath and storing it in a character field.
      * This example uses IBM i 7.1+ prototypes for most calls; refer to
      *  the individual documentation for these subprocedures for the 
      *  6.1 prototypes.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gRootDomDS      DS                  LikeDS(RXS_OpenDomDS_t) 
     D gXml            S                   Like(RXS_Var8Kv_t)
     D gData           S                   Like(RXS_Var8Kv_t)
      /free
       RXS_ResetDS( gRootDomDS : RXS_DS_TYPE_OPENDOM );
       
       gXml = RXS_GetStdIn();
       gRootDomDS = RXS_OpenDom( gXml );

       gXPath = RXS_XPath( '/*:bookstore/*:book' );
       gData = RXS_ParseDomToText( gXPath : gRootDomDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_ParseDomDS_t

```rpgle
     D RXS_ParseDomDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specifies the CCSID of the XML being parsed.
     D   InputCcsid...
     D                               10I 0

      // Specifies the CCSID the parsed data will be converted to.
     D   OutputCcsid...
     D                               10I 0

      // Specifies an IFS path to an XML file to parse instead of the Input parm.
     D   Stmf...
     D                                     Like(RXS_Var1Kv_t)

      // Contains the current count of XML nodes tracked by this data structure.
     D   NodeCount                   10U 0

     D   NodeType                    10I 0

      // Internal use only
     D   xmlPtr                        *

      // Internal use only
     D   docPtr                        *

      // Internal use only
     D   contextPtr                    *

      // Internal use only
     D   dictPtr                       *

      // Internal use only
     D   currentPtr                    *

      // Internal use only
     D   parentPtr                     *

      // Internal use only
     D   Reserved                  4096A
```



---

# RXS_ParseDomToXml()

> Serializes an XML node matching an XPath expression back to an XML string.

This subprocedure retrieves the raw XML from a specific XPath.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the XML subsection retrieved by the specified XPath.
     D RXS_ParseDomToXml...
     D                 PR                  Extproc('RXS_ParseDomToXml') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // XPath used to determine which nodes are retrieved into the
      //   RXS_ParseDomDS_t return data structure.
     D  XPath                              Like(RXS_Var8Kv_t) Const
     D                                     Options(*Varsize)

      // RXS_ParseDomDS_t data structure, e.g. the "parent" structure.
     D  DS                                 Like(RXS_ParseDomDS_t)
     D                                     Options(*Varsize)
```


### IBM i 6.1

```rpgle
     D RXS_ParseDomToXml...
     D                 PR                  Extproc('RXS_ParseDomToXml') Opdesc

      // Holds the XML subsection retrieved by the specified XPath.
     D  Output                             Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize)

      // XPath used to determine which nodes are retrieved into the
      //   RXS_ParseDomDS_t return data structure.
     D  XPath                              Like(RXS_Var8Kv_t) Const
     D                                     Options(*Varsize)

      // RXS_ParseDomDS_t data structure, e.g. the "parent" structure.
     D  DS                                 Like(RXS_ParseDomDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates retrieving the raw XML data from a specified  
      *  XPath and storing it in a character field.
      * This example uses IBM i 7.1+ prototypes for most calls; refer to
      *  the individual documentation for these subprocedures for the 
      *  6.1 prototypes.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gRootDomDS      DS                  LikeDS(RXS_OpenDomDS_t)
     D gXml            S                   Like(RXS_Var8Kv_t)
     D gData           S                   Like(RXS_Var8Kv_t)
      /free
       RXS_ResetDS( gRootDomDS : RXS_DS_TYPE_OPENDOM );
       
       gXml = RXS_GetStdIn();
       gRootDomDS = RXS_OpenDom( gXml );

       gXPath = RXS_XPath( '/*:bookstore/*:book' );
       gData = RXS_ParseDomToXml( gXPath : gRootDomDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_ParseDomDS_t

```rpgle
     D RXS_ParseDomDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specifies the CCSID of the XML being parsed.
     D   InputCcsid...
     D                               10I 0

      // Specifies the CCSID the parsed data will be converted to.
     D   OutputCcsid...
     D                               10I 0

      // Specifies an IFS path to an XML file to parse instead of the Input parm.
     D   Stmf...
     D                                     Like(RXS_Var1Kv_t)

      // Contains the current count of XML nodes tracked by this data structure.
     D   NodeCount                   10U 0

     D   NodeType                    10I 0

      // Internal use only
     D   xmlPtr                        *

      // Internal use only
     D   docPtr                        *

      // Internal use only
     D   contextPtr                    *

      // Internal use only
     D   dictPtr                       *

      // Internal use only
     D   currentPtr                    *

      // Internal use only
     D   parentPtr                     *

      // Internal use only
     D   Reserved                  4096A
```



---

# RXS_ParseJson()

> Event-based JSON parser that calls a handler subprocedure for each token (string, number, boolean, null, array/object start and end).

This subprocedure provides event-based parsing for JSON documents. Using this subprocedure requires a handler subprocedure.

All JSON parsing subprocedures must have the following prototype:

```rpgle
     D JsonHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
```

The JSON parser recognizes the following element events:

| Event | pType Constant | pPath Format | Example |
| ----- | -------------- | ------------ | ------- |
| Array Start | `RXS_JSON_ARRAY` | `[*]` | `/phone[*]` |
| Array End | `RXS_JSON_ARRAY_END` | `[*]` | `/phone[*]` |
| Object Start | `RXS_JSON_OBJECT` | `/` | `/phone` |
| Object End | `RXS_JSON_OBJECT_END` | `/` | `/phone` |

Because the start and end events share the same path structure, you will also need to check the value of the pType parameter.

By default, the JSON parser returns all values as character data. In order to retrieve values in their respective RPG data types, you must set the value of ConvertDataToString to RXS_NO in the RXS_ParseJsonDS_t data structure. The JSON parser recognizes the following content types:

| JSON Data Type | pType Constant | RPG Data Type |
| --------- | -------------- | ------------- |
| String | `RXS_JSON_STRING` | `A` |
| Boolean | `RXS_JSON_BOOLEAN` | `N` |
| Integer | `RXS_JSON_INTEGER` | `20I 0` |
| Double | `RXS_JSON_DOUBLE` | `8F` |
| Null | `RXS_JSON_NULL` | n/a |

For a detailed example of JSON parsing, please see this blog post: [Parsing JSON with RPG-XML Suite 3.3](https://www.katointegrations.com/2016/11/parsing-json-with-rpg-xml-suite-3-3/).

This subprocedure can optionally return a RXS_JsonStructureDS_t data structure which can be used with the RXS JSON composition subprocedures.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns an RXS_JsonStructureDS_t which can be used by the RXS JSON
      //   composition subprocedures.
     D RXS_ParseJson...
     D                 PR                  Extproc('RXS_ParseJson') Opdesc
     D                                     LikeDS(RXS_JsonStructureDS_t)

      // Holds the JSON data to be passed to the parsing subprocedure. Will be
      //   ignored if the Stmf subfield of the pParseJsonDS parameter is set.
     D  pInput                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Omit:*Varsize)

      // RXS_ParseJsonDS_t data structure which controls how RXS_ParseJson()
      //   functions.
     D  pParseJsonDS                       LikeDS(RXS_JsonParseDS_t)
```


## Example Code

### IBM i 7.1+

#### Example 1: Simple Parse

```rpgle
      *--------------------------------------------------------------
      * This example parses a simple JSON structure stored in the
      * field 'JSON' and stores the data contained in fields 'id',
      * 'name', and 'price' in the corresponding global fields.
      *
      * At a high level, the JSON is parsed into the JsonHandler()
      * in small chunks. Then, a SELECT block is used to detect whether
      * or not the current JSON element matches one you'd like to
      * handle. If it does, you can extract the data using RXS_STR().
      *
      * Once the JSON has been entirely passed through JsonHandler(),
      * control returns to the main portion of the program.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JsonHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const

     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
     D JSON            S                   Like(RXS_Var64Kv_t)
     D id              S                   Like(RXS_Var1Kv_t)
     D name            S                   Like(RXS_Var1Kv_t)
     D price           S              6P 2
      /free
        // JSON structure looks like this:
        // {
        //    "item": {
        //      "id": 7,
        //      "name": "headlight fluid",
        //      "price": 12.50
        //    }
        // }
        JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
         '"price": 12.50 } }';

        RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
        ParseJsonDS.Handler = %Paddr( JsonHandler );
        RXS_ParseJson( JSON : ParseJsonDS );

        RXS_JobLog( 'id: %s' : id );
        RXS_JobLog( 'name: %s' : name );
        RXS_JobLog( 'price: %s' : %Char(price) );

        *INLR = *ON;
      /end-free


     P JsonHandler     B                   Export
     D                 PI              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
      /free

       select;
         when pPath = '/item/id';
           // All data in the JSON being parsed is returned
           //  as a character data field. If you need the data
           //  to be in a different data type, you can use %Int()
           //  or %Dec() to further convert it as shown further
           //  below.
           id = RXS_STR( pData : pDataLen );

         when pPath = '/item/name';
           name = RXS_STR( pData : pDataLen );

         when pPath = '/item/price';
           monitor;
             price = %Dec( RXS_STR( pData : pDataLen ) : 6 : 2 );
           on-error 105;
             RXS_JobLog( 'Error converting price!' );
             return *Off;
           endmon;


       endsl;

       return *On;
      /end-free
     P                 E
```

#### Example 2: Parse JSON Data Types

```rpgle
      *--------------------------------------------------------------
      * This example parses a simple JSON structure stored in the
      * field 'JSON' and stores the data contained in fields 'id',
      * 'name', and 'price' in the corresponding global fields.
      *
      * This example differs from Example 1 in that instead of
      * returning all values as character data, values will be
      * returned using the RPG equivalents of the JSON data types.
      *
      * Doing this is somewhat more complicated, and may not be useful
      * in many JSON parsing situations, so this is not the default
      * behavior of RXS_ParseJson().
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JsonHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const

     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
     D JSON            S                   Like(RXS_Var64Kv_t)

     D id              S             20I 0
     D name            S                   Like(RXS_Var1Kv_t)
     D price           S              6P 2
     D sale            S               N

      /free
        // JSON structure looks like this:
        // {
        //    "item": {
        //      "id": 7,
        //      "name": "headlight fluid",
        //      "price": 12.50,
        //      "sale": true
        //    }
        // }
        JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
         '"price": 12.50, "sale": true } }';

        RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
        ParseJsonDS.Handler = %Paddr( JsonHandler );
        // By default this value is RXS_YES, meaning that all data
        //  returned will be converted into character data before
        //  it is available inside JsonHandler. Setting this to RXS_NO
        //  means that JsonHandler will instead be receiving RPG data
        //  types beyond character fields, such as integers, indicators,
        //  and floating point numbers.
        ParseJsonDS.ConvertDataToString = RXS_NO;
        RXS_ParseJson( JSON : ParseJsonDS );

        RXS_JobLog( 'id: %s' : %Char(id) );
        RXS_JobLog( 'name: %s' : name );
        RXS_JobLog( 'price: %s' : %Char(price) );
        if sale;
          RXS_JobLog( 'sale: %s' : 'true' );
        else;
          RXS_JobLog( 'sale: %s' : 'false' );
        endif;

        *INLR = *ON;
      /end-free


     P JsonHandler     B                   Export
     D                 PI              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
      * To return native JSON types, we have to make use of Basing pointers
      *  and pay careful attention to the pType parameter. Please note that
      *  JSON has two types of numbers - integer and float. Floating point
      *  numbers are not precise, and values returned by RXS_ParseJson
      *  for floating-point numbers may differ slighltly from how they
      *  appear in the JSON. If your JSON data is going to contain
      *  decimal values it is advised to parse JSON using the default
      *  behavior where all parsed data is converted to character data
      *  before being returned to avoid a loss of precision.
     D BoolVal         S               N   Based(pData)
     D IntVal          S             20I 0 Based(pData)
     D FloatVal        S              8F   Based(pData)
      /free

       select;
         when pPath = '/item/id';
           // We know that the 'id' field is an integer, so we can simply
           //  do the following:
           id = IntVal;
           // Note that integers are always going to be returned as 8 byte
           //  integers, e.g. 20I 0. Using 3I 0, 5I 0, or 10I 0 will not work.

         when pPath = '/item/name';
           // 'name' is character data, and should be handled the same way
           name = RXS_STR( pData : pDataLen );

         when pPath = '/item/price';
           // 'price' is stored in the JSON as a decimal. We know from context
           //  that price is denoting a monetary value, which is a situation
           //  where the loss of precision using a floating point number brings
           //  is completely unacceptable. For example, instead of returning
           //  12.50 as we would expect, we might get 12.501 or 12.499. This
           //  would make a large difference to a business! However, as this
           //  is example code, below demonstrates returning floating point
           //  numbers:
           price = FloatVal;

         when pPath = '/item/sale';
           // We know that the 'id' field is a boolean value. It's important to
           //  know that a JSON character data value of 'true' is different from
           //  the JSON boolean data value of true. The two items below are not
           //  equivalent:
           //
           //  {
           //    "item1": 'true',
           //    "item2" : true
           //  }
           //  The RPG equivalent of a boolean data type is an indicator field.
           sale = BoolVal;
       endsl;
       return *On;
      /end-free
     P                 E
```

### IBM i 6.1

#### Example 1: Simple Parse

```rpgle
      *--------------------------------------------------------------
      * This example parses a simple JSON structure stored in the
      * field 'JSON' and stores the data contained in fields 'id',
      * 'name', and 'price' in the corresponding global fields.
      *
      * At a high level, the JSON is parsed into the JsonHandler()
      * in small chunks. Then, a SELECT block is used to detect whether
      * or not the current JSON element matches one you'd like to
      * handle. If it does, you can extract the data using RXS_STR().
      *
      * Once the JSON has been entirely passed through JsonHandler(),
      * control returns to the main portion of the program.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D JsonHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const

     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
     D JSON            S                   Like(RXS_Var64Kv_t)
     D id              S                   Like(RXS_Var1Kv_t)
     D name            S                   Like(RXS_Var1Kv_t)
     D price           S              6P 2
      /free
        // JSON structure looks like this:
        // {
        //    "item": {
        //      "id": 7,
        //      "name": "headlight fluid",
        //      "price": 12.50
        //    }
        // }
        JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
         '"price": 12.50 } }';

        RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
        ParseJsonDS.Handler = %Paddr( JsonHandler );
        RXS_ParseJson( JSON : ParseJsonDS );

        RXS_JobLog( 'id: %s' : id );
        RXS_JobLog( 'name: %s' : name );
        RXS_JobLog( 'price: %s' : %Char(price) );

        *INLR = *ON;
      /end-free


     P JsonHandler     B                   Export
     D                 PI              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
      *
     D ParsedData      S                   Like(RXS_Var1Kv_t)
      /free

       select;
         when pPath = '/item/id';
           // All data in the JSON being parsed is returned
           //  as a character data field. If you need the data
           //  to be in a different data type, you can use %Int()
           //  or %Dec() to further convert it as shown further
           //  below.
           RXS_STR( ParsedData : pData : pDataLen );
           id = ParsedData;

         when pPath = '/item/name';
           RXS_STR( id : pData : pDataLen );
           name = ParsedData;

         when pPath = '/item/price';
           ParsedData = RXS_STR( pData : pDataLen );
           monitor;
             price = %Dec( ParsedData : 6 : 2 );
           on-error 105;
             price = 0;
             RXS_JobLog( 'Error converting price!' );
             return *Off;
           endmon;
       endsl;
       return *On;
      /end-free
     P                 E
```

#### Example 2: Parse JSON Data Types

```rpgle
      *--------------------------------------------------------------
      * This example parses a simple JSON structure stored in the
      * field 'JSON' and stores the data contained in fields 'id',
      * 'name', and 'price' in the corresponding global fields.
      *
      * This example differs from Example 1 in that instead of
      * returning all values as character data, values will be
      * returned using the RPG equivalents of the JSON data types.
      *
      * Doing this is somewhat more complicated, and may not be useful
      * in many JSON parsing situations, so this is not the default
      * behavior of RXS_ParseJson().
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D JsonHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const

     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
     D JSON            S                   Like(RXS_Var64Kv_t)

     D id              S             20I 0
     D name            S                   Like(RXS_Var1Kv_t)
     D price           S              6P 2
     D sale            S               N

      /free
        // JSON structure looks like this:
        // {
        //    "item": {
        //      "id": 7,
        //      "name": "headlight fluid",
        //      "price": 12.50,
        //      "sale": true
        //    }
        // }
        JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
         '"price": 12.50, "sale": true } }';

        RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
        ParseJsonDS.Handler = %Paddr( JsonHandler );
        // By default this value is RXS_YES, meaning that all data
        //  returned will be converted into character data before
        //  it is available inside JsonHandler. Setting this to RXS_NO
        //  means that JsonHandler will instead be receiving RPG data
        //  types beyond character fields, such as integers, indicators,
        //  and floating point numbers.
        ParseJsonDS.ConvertDataToString = RXS_NO;
        RXS_ParseJson( JSON : ParseJsonDS );

        RXS_JobLog( 'id: %s' : %Char(id) );
        RXS_JobLog( 'name: %s' : name );
        RXS_JobLog( 'price: %s' : %Char(price) );
        if sale;
          RXS_JobLog( 'sale: %s' : 'true' );
        else;
          RXS_JobLog( 'sale: %s' : 'false' );
        endif;

        *INLR = *ON;
      /end-free


     P JsonHandler     B                   Export
     D                 PI              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
      * To return native JSON types, we have to make use of Basing pointers
      *  and pay careful attention to the pType parameter. Please note that
      *  JSON has two types of numbers - integer and float. Floating point
      *  numbers are not precise, and values returned by RXS_ParseJson
      *  for floating-point numbers may differ slighltly from how they
      *  appear in the JSON. If your JSON data is going to contain
      *  decimal values it is advised to parse JSON using the default
      *  behavior where all parsed data is converted to character data
      *  before being returned to avoid a loss of precision.
     D BoolVal         S               N   Based(pData)
     D IntVal          S             20I 0 Based(pData)
     D FloatVal        S              8F   Based(pData)
      /free

       select;
         when pPath = '/item/id';
           // We know that the 'id' field is an integer, so we can simply
           //  do the following:
           id = IntVal;
           // Note that integers are always going to be returned as 8 byte
           //  integers, e.g. 20I 0. Using 3I 0, 5I 0, or 10I 0 will not work.

         when pPath = '/item/name';
           // 'name' is character data, and should be handled the same way
           name = RXS_STR( pData : pDataLen );

         when pPath = '/item/price';
           // 'price' is stored in the JSON as a decimal. We know from context
           //  that price is denoting a monetary value, which is a situation
           //  where the loss of precision using a floating point number brings
           //  is completely unacceptable. For example, instead of returning
           //  12.50 as we would expect, we might get 12.501 or 12.499. This
           //  would make a large difference to a business! However, as this
           //  is example code, below demonstrates returning floating point
           //  numbers:
           price = FloatVal;

         when pPath = '/item/sale';
           // We know that the 'id' field is a boolean value. It's important to
           //  know that a JSON character data value of 'true' is different from
           //  the JSON boolean data value of true. The two items below are not
           //  equivalent:
           //
           //  {
           //    "item1": 'true',
           //    "item2" : true
           //  }
           //  The RPG equivalent of a boolean data type is an indicator field.
           sale = BoolVal;
       endsl;
       return *On;
      /end-free
     P                 E
```

## Data Structures

### RXS_ParseJsonDS_t

```rpgle
     D RXS_ParseJsonDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_PARSEJSON)

     D   OnErrorMessageType...
     D                                5I 0

      // Holds a PROCPTR to a JsonHandler subprocedure to handle all possible
      //   parsed JSON events.
     D   Handler...
     D                                 *   Procptr

      // Internal use only
     D   InputPointer...
     D                                 *   Inz(*Null)

      // Internal use only
     D   InputLength...
     D                               10U 0 Inz(0)

      // Specifies the CCSID of the JSON being parsed.
      // Default: Job CCSID
     D   InputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies the CCSID the parsed data will be converted to.
      // Default: Job CCSID
     D   OutputCcsid...
     D                               10I 0 Inz(RXS_CCSID_JOB)

      // Specifies an IFS path to a JSON file to parse instead of the pInput
      //   parm.
     D   Stmf...
     D                                     Like(RXS_Var8Kv_t)

      // Internal use only
     D   ConvertDataToString...
     D                                 N   Inz(RXS_YES)
```



---

# RXS_ProcessStmf()

> Processes an IFS stream file in chunks, calling a handler subprocedure for each chunk.

This subprocedure can be used to create, copy, move, or delete a specified IFS stream file.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ProcessStmf...
     D                 PR                  Extproc('RXS_ProcessStmf') Opdesc

      // Contains the IFS file path as well as configuration options to control
      //   how the data is written. May be one of a few possible data structures,
      //   listed below.
     D   DS                                Like(RXS_Var64K_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 6.1+

#### Example 1: Create an IFS Stream File

```rpgle
      *--------------------------------------------------------------
      * This example code creates a stream file named new_file.xml in 
      * the IFS directory /tmp with CCSID 819.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D CreateStmfDS    DS                  LikeDS(RXS_CreateStmfDS_t)
     D                                     Inz(*LikeDS)
      /free

        RXS_ResetDS( CreateStmfDS : RXS_DS_TYPE_CREATESTMF );
        CreateStmfDS.Stmf = '/tmp/new_file.xml';
        CreateStmfDS.Ccsid = RXS_CCISD_ISO88591;
        RXS_ProcessStmf( CreateStmfDS );

        *INLR = *ON;
      /end-free
```

#### Example 2: Copy an IFS Stream File

```rpgle
      *--------------------------------------------------------------
      * This example code copies an IFS file from /tmp/new_file.xml to 
      * /tmp/copied_new_file.xml.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D CopyStmfDS      DS                  LikeDS(RXS_CopyStmfDS_t)
     D                                     Inz(*LikeDS)
      /free

        RXS_ResetDS( CopyStmfDS : RXS_DS_TYPE_COPYSTMF );
        CopyStmfDS.FromStmf = '/tmp/new_file.xml';
        CopyStmfDS.ToStmf = '/tmp/copied_new_file.xml';
        RXS_ProcessStmf( CopyStmfDS );

        *INLR = *ON;
      /end-free
```

#### Example 3: Move an IFS Stream File

```rpgle
      *--------------------------------------------------------------
      * This example code copies an IFS file from /tmp/new_file.xml 
      * to /tmp/moved_new_file.xml.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D MoveStmfDS      DS                  LikeDS(RXS_MoveStmfDS_t)
     D                                     Inz(*LikeDS)
      /free

        RXS_ResetDS( MoveStmfDS : RXS_DS_TYPE_MOVESTMF );
        MoveStmfDS.FromStmf = '/tmp/new_file.xml';
        MoveStmfDS.ToStmf = '/tmp/moved_new_file.xml';
        RXS_ProcessStmf( MoveStmfDS );

        *INLR = *ON;
      /end-free
```

#### Example 4: Delete an IFS Stream File

```rpgle
      *--------------------------------------------------------------
      * This example code deletes an IFS file named /tmp/moved_new_file.xml.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D DeleteStmfDS    DS                  LikeDS(RXS_DeleteStmfDS_t)
     D                                     Inz(*LikeDS)
      /free

        RXS_ResetDS( DeleteStmfDS : RXS_DS_TYPE_DELETESTMF );
        DeleteStmfDS.Stmf = '/tmp/moved_new_file.xml';
        RXS_ProcessStmf( DeleteStmfDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_CopyStmfDS_t

```rpgle
     D RXS_CopyStmfDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specify the IFS file path to copy from.
     D   FromStmf                          LIKE(RXS_Var1Kv_t)

      // Specify the IFS file path to copy to.
     D   ToStmf                            LIKE(RXS_Var1Kv_t)

      // Specify the CCSID the copied file will use.
     D   ToCcsid                     10I 0

      // Internal use only
     D   Reserved                  2048A
```


### RXS_CreateStmfDS_t

```rpgle
     D RXS_CreateStmfDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specify IFS path where the file will be created.
     D   Stmf                              LIKE(RXS_Var1Kv_t)

      // Specify the CCSID to create the file with.
     D   Ccsid                       10I 0

      // Internal use only
     D   Reserved                  2048A
```


### RXS_DeleteStmfDS_t

```rpgle
     D RXS_DeleteStmfDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specify the IFS file path to delete.
     D   Stmf                              LIKE(RXS_Var1Kv_t)

      // Internal use only
     D   Reserved                  2048A
```


### RXS_MoveStmfDS_t

```rpgle
     D RXS_MoveStmfDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specify the IFS file path to move from.
     D   Stmf                              LIKE(RXS_Var1Kv_t)

      // Specify an IFS file directory to move to. Note: You should only specify
      //   a directory path, not a full path to a file.
     D   ToDir                             LIKE(RXS_Var1Kv_t)

      // Internal use only
     D   Reserved                  2048A
```



---

# RXS_PutEnvVar()

> Sets an Apache environment variable from within an RPG program.

This subprocedure is used to set a job-level environment variable, or to modify an already existing environment variable.

## Subprocedure Prototype

### IBM i 6.1

```rpgle
     D RXS_PutEnvVar...
     D                 PR

      // Specifies the environment variable to be set. Must be in name=value
      //   format.
     D   EnvVar                            Like(RXS_Var16Mv_t) Const
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 6.1+

#### Example 1: Setting an Environment Variable

```rpgle
      *--------------------------------------------------------------
      * This example code sets an environment variable. You can view 
      * the results by running the following command:
      *  WRKENVVAR
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D EnvVar          S                   Like(RXS_Var1Kv_t)
     D Data            S                   Like(RXS_Var1Kv_t)

      /free

       EnvVar = 'IS_RPG_AWESOME=Yes';
       RXS_PutEnvVar( EnvVar );
       *INLR = *ON;
      /end-free
```


---

# RXS_PutStdOut()

> Writes data to the HTTP response body (stdout); used to send output from hosted web service programs.

This subprocedure is used to output (to standard out) data. Typically, this would be used in a scenario where you are providing a webservice, and would be used to output XML retrieved via [RXS_GetComposeBuffer()](https://isupport.katointegrations.com/rxs/3.3/rxs_getcomposebuffer.md).

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_PutStdOut...
     D                 PR                  Extproc('RXS_PutStdOut')

      // Data to be written to "standard out", i.e. to a web browser or to a web
      //   service which initiated the incoming request as a client.
     D   Input                             Like(RXS_Var16Mv_t)
     D                                     Options(*Varsize:*Omit) Const

      // Optional RXS_PutStdOutDS_t to allow for CCSID conversion during output.
     D   pDS                               Like(RXS_Var64K_t)
     D                                     Options(*Varsize:*Nopass)
```


## Example Code

### IBM i 6.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code writes the content of the field gXml to STDOUT.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gXML            S                   Like(RXS_Var1Kv_t)
      /free
       gXml = '<element>Example XML</element>';
       RXS_PutStdOut( gXml );

       *INLR = *ON;
      /end-free
```

#### Example 2

```rpgle
      *--------------------------------------------------------------
      * This example code specifies the header content type as XML,
      *  then sends the data to STDOUT.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D PutStdOutDS     DS                  LikeDS(RXS_PutStdOutDS_t)

     D gXML            S                   Like(RXS_Var1Kv_t)
      /free
       RXS_ResetDS( PutStdOutDS : RXS_DS_TYPE_PUTSTDOUT ); 

       gXml = '<element>Example XML</element>';
       
       PutStdOutDS.HeaderContentType = 'text/xml';
       RXS_PutStdOut( gXml : PutStdOutDS );

       *INLR = *ON;
      /end-free
```

#### Example 3

```rpgle
      *--------------------------------------------------------------
      * This example code specifies the header content type as XML, and
      *  specifies the IFS file from which data will be read to STDOUT.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D PutStdOutDS     DS                  LikeDS(RXS_PutStdOutDS_t)
      /free
       RXS_ResetDS( PutStdOutDS : RXS_DS_TYPE_PUTSTDOUT ); 
       PutStdOutDS.HeaderContentType = 'text/xml';
       PutStdOutDS.Stmf = '/tmp/over16mb.xml';
       RXS_PutStdOut( *Omit : PutStdOutDS );

       *INLR = *ON;
      /end-free
```

#### Example 4

```rpgle
      *--------------------------------------------------------------
      * This example code specifies the header content type as XML, and 
      *  sets a header status code and text to communicate an HTTP error,
      *  then sends the XML to STDOUT.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D PutStdOutDS     DS                  LikeDS(RXS_PutStdOutDS_t)

     D gXML            S                   Like(RXS_Var1Kv_t)
      /free
       RXS_ResetDS( PutStdOutDS : RXS_DS_TYPE_PUTSTDOUT );

       gXml = '<element>Example XML</element>';
       
       PutStdOutDS.HeaderStatusCode = 500;
       PutStdOutDS.HeaderStatusText = 'SOAP Fault'; 
       PutStdOutDS.HeaderContentType = 'text/xml';
       RXS_PutStdOut( gXml : PutStdOutDS );

       *INLR = *ON;
      /end-free
```

#### Example 5

```rpgle
      *--------------------------------------------------------------
      * This example code specifies the header content type as XML, and 
      *  sets a sets a custom header "Prefer", then sends the XML to STDOUT.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D PutStdOutDS     DS                  LikeDS(RXS_PutStdOutDS_t)

     D gXML            S                   Like(RXS_Var1Kv_t)
      /free
       RXS_ResetDS( PutStdOutDS : RXS_DS_TYPE_PUTSTDOUT );

       gXml = '<element>Example XML</element>';
       
       PutStdOutDS.HeaderContentType = 'text/xml';
       PutStdOutDS.CustomHeaderName(1) = 'Prefer'; 
       PutStdOutDS.CustomHeaderValue(1) = 'Return=representation';
       RXS_PutStdOut( gXml : PutStdOutDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_PutStdOutDS_t

```rpgle
     D RXS_PutStdOutDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_PUTSTDOUT)

     D   OnErrorMessageType...
     D                                5I 0

      // The contents of the Input parameter are converted from the job CCSID to
      //   this CCSID before being sent to standard out.
     D   Ccsid                       10I 0

      // Specifies IFS path to a stream file whose contents are to be sent to
      //   STDOUT.
     D   Stmf                              Like(RXS_Var1Kv_t)

      // The HTTP Status code to be sent in the response. A sucessful response is
      //   typically "200".
     D   HeaderStatusCode...
     D                               10I 0

      // The HTTP Status text to be sent in the response. This is also known as
      //   the reason phrase. A sucessful response is typically "OK".
     D   HeaderStatusText...
     D                                     Like(RXS_Var1Kv_t)

      // The HTTP content type to be sent with the response. The most common
      //   content type for web services is "text/xml". There is no assumed default
      //   value and if this value is not supplied via this data structure, then
      //   the content type is usually required to be supplied in the Input
      //   parameter and it must be followed by two line control characters.
     D   HeaderContentType...
     D                                     Like(RXS_Var1Kv_t)

      // Specifies the name of up to 50 custom HTTP headers sent with the
      //   response.
     D   CustomHeaderName...
     D                               64A   Varying Dim(50)

      // Specifies the value of up to 50 corresponding CustomHeaderName's.
     D   CustomHeaderValue...
     D                                     Like(RXS_Var1Kv_t) Dim(50)
```



---

# RXS_PutStmf()

> Writes data from an RPG variable to an IFS stream file.

This subprocedure writes the contents of a field up to 16MB in size to an IFS stream file.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_PutStmf...
     D                 PR                  Extproc('RXS_PutStmf') Opdesc

      // Data to write to the IFS stream file.
     D  pInput                             Like(RXS_Var16Mv_t) Const
     D                                     Options(*Omit:*Varsize)

      // RXS_PutStmfDS_t used to configure the write operation, such as the file
      //   path to write to.
     D  pDS                                LikeDS(RXS_PutStmfDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 7.1+

#### Example 1: Output Character String to IFS stream file

```rpgle
      *--------------------------------------------------------------
      * This example code writes the value <data>The quick brown fox 
      * jumps over the lazy dog</data> to the file /tmp/rxs_putstmf.txt
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D PutStmfDS       DS                  LikeDS(RXS_PutStmfDS_t)
     D                                     Inz(*LikeDS)
     D Data            S                   Like(RXS_Var64Kv_t)
      /free

       Data = '<data>The quick brown fox jumps over the lazy dog</data>';

       RXS_ResetDS( PutStmfDS : RXS_DS_TYPE_PUTSTMF );
       PutStmfDS.Stmf = '/tmp/rxs_putstmf.txt';
       RXS_PutStmf( Data : PutStmfDS );

       *INLR = *ON;
       
      /end-free
```

## Data Structures

### RXS_PutStmfDS_t

```rpgle
     D RXS_PutStmfDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_PUTSTMF)

     D   OnErrorMessageType...
     D                               10I 0

      // Specifies an IFS path to a stream file to write to.
     D   Stmf                              LIKE(RXS_Var1Kv_t)

      // Determines whether to append the data to the file (RXS_YES), or to
      //   overwrite the file (RXS_NO).
      // Valid values: RXS_YES, RXS_NO
      // Default: RXS_NO
     D   Append                        N

      // The CCSID of the IFS stream file to be created/written to.
     D   StmfCcsid                   10I 0

      // The CCSID of the data being passed. Note: This is typically optional as
      //   internally RXS_PutStmf() will assume the data you're passing is the same
      //   CCSID as the current job.
     D   InputCcsid                  10I 0

      // Internal use only
     D   InputPointer...
     D                                 *   Inz(*Null)

      // Internal use only
     D   InputLength                 10I 0
```



---

# RXS_ResetDS()

> Resets all fields in an RXS data structure to their default values.

This subprocedure initializes a data structure with default values. Always intialize a data structure before use and always use this API instead of the RESET opcode.

A full list of DS-type constants is available in the RXSCB copybook.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_ResetDS...
     D                 PR                  Extproc('RXS_ResetDS') Opdesc

      // Holds the data structure to be initialized with default values.
     D  DS                                 Like(RXS_Var512K_t)
     D                                     Options(*Varsize)

      // The type of data structure to be initialized with default values.
     D  TypeOfDS                      5I 0 Const
```


## Example Code

### IBM i 6.1+

#### Example 1: Intialize all current RXS3 data structure types

```rpgle
      *--------------------------------------------------------------
      * This example code initializes most of the current RXS3 data 
      * structure types using RXS_ResetDS().
      *--------------------------------------------------------------
      * Data structures for web service APIs
      * Consuming a web service
     D gTransmitDS     DS                  LikeDS(RXS_TransmitDS_t)
      * Offering a web service
     D gGetStdInDS     DS                  LikeDS(RXS_GetStdInDS_t)
     D gPutStdOutDS    DS                  LikeDS(RXS_PutStdOutDS_t)

      * Data structures for XML APIs
     D gComposeDS      DS                  LikeDS(RXS_ComposeDS_t)
     D gParseDS        DS                  LikeDS(RXS_ParseDS_t)
     D gXsdValidationDS...
     D                 DS                  LikeDS(RXS_XsdValidationDS_t)
      * XML DOM parser
     D gOpenDomDS      DS                  LikeDS(RXS_OpenDomDS_t)
     D gParseDomDS     DS                  LikeDS(RXS_ParseDomDS_t)

      * Data structures for JSON APIs
     D gComposeJsonDS  DS                  LikeDS(RXS_ComposeJsonDS_t)
     D gParseJsonDS    DS                  LikeDS(RXS_ParseJsonDS_t)

      * RXS_Convert data structures
     D gConvertBase64DS...
     D                 DS                  LikeDS(RXS_ConvertBase64DS_t)
     D gConvertCaseDS  DS                  LikeDS(RXS_ConvertCaseDS_t)
     D gConvertCcsidDS...
     D                 DS                  LikeDS(RXS_ConvertCcsidDS_t)
     D gConvertUrlPercentDS...
     D                 DS                  LikeDS(RXS_ConvertUrlPercentDS_t)
     D gConvertUserDefinedDS...
     D                 DS                  LikeDS(RXS_ConvertUserDefinedDS_t)
     D gConvertXmlEntitiesDS...
     D                 DS                  LikeDS(RXS_ConvertXmlEntitiesDS_t)

      * RXS_Crypt data structures
     D gAesCryptDS     DS                  LikeDS(RXS_AesCryptDS_t)
     D gMd5CryptDS     DS                  LikeDS(RXS_Md5CryptDS_t)
     D gShaCryptDS     DS                  LikeDS(RXS_ShaCryptDS_t)

      * Data structures for STMF APIs
     D gCopyStmfDS     DS                  LikeDS(RXS_CopyStmfDS_t)
     D gCreateStmfDS   DS                  LikeDS(RXS_CreateStmfDS_t)
     D gDeleteStmfDS   DS                  LikeDS(RXS_DeleteStmfDS_t)
     D gGetStmfDS      DS                  LikeDS(RXS_GetStmfDS_t)
     D gPutStmfDS      DS                  LikeDS(RXS_PutStmfDS_t)
     D gMoveStmfDS     DS                  LikeDS(RXS_MoveStmfDS_t)

      * Error handling APIs
     D gCatchThrowErrorDS...
     D                 DS                  LikeDS(RXS_CatchThrowErrorDS_t)

      /free
      
       // Data structures for web service APIs
       //  Consuming a web service
       RXS_ResetDS( gTransmitDS : RXS_DS_TYPE_TRANSMIT );
       //  Offering a web service
       RXS_ResetDS( gGetStdInDS : RXS_DS_TYPE_GETSTDIN );
       RXS_ResetDS( gPutStdOutDS : RXS_DS_TYPE_PUTSTDOUT );
       
       // Data structures for XML APIs
       RXS_ResetDS( gComposeDS : RXS_DS_TYPE_COMPOSE );
       RXS_ResetDS( gParseDS : RXS_DS_TYPE_PARSE );
       RXS_ResetDS( gXsdValidationDS : RXS_DS_TYPE_XSDVALIDATION );
       //  XML DOM parser
       RXS_ResetDS( gOpenDomDS : RXS_DS_TYPE_OPENDOM );
       RXS_ResetDS( gParseDomDS : RXS_DS_TYPE_PARSEDOM );
       
       // Data structures for JSON APIs
       RXS_ResetDS( gComposeJsonDS : RXS_DS_TYPE_COMPOSEJSON );
       RXS_ResetDS( gParseJsonDS : RXS_DS_TYPE_PARSEJSON );

       // RXS_Convert data structures
       RXS_ResetDS( gConvertBase64DS : RXS_DS_TYPE_CONVERTBASE64 );
       RXS_ResetDS( gConvertCaseDS : RXS_DS_TYPE_CONVERTCASE );
       RXS_ResetDS( gConvertCcsidDS : RXS_DS_TYPE_CONVERTCCSID );
       RXS_ResetDS( gConvertUrlPercentDS : RXS_DS_TYPE_CONVERTURLPERCENT );
       RXS_ResetDS( gConvertUserDefinedDS : RXS_DS_TYPE_CONVERTUSERDEFINED );
       RXS_ResetDS( gConvertXmlEntitiesDS : RXS_DS_TYPE_CONVERTXMLENTITIES );
       
       // RXS_Crypt data structures
       RXS_ResetDS( gAesCryptDS : RXS_DS_TYPE_AESCRYPT );
       RXS_ResetDS( gMd5CryptDS : RXS_DS_TYPE_MD5CRYPT );
       RXS_ResetDS( gShaCryptDS : RXS_DS_TYPE_SHACRYPT );
       
       // Data structures for STMF APIs
       RXS_ResetDS( gCopyStmfDS : RXS_DS_TYPE_COPYSTMF );
       RXS_ResetDS( gCreateStmfDS : RXS_DS_TYPE_CREATESTMF );
       RXS_ResetDS( gDeleteStmfDS : RXS_DS_TYPE_DELETESTMF );
       RXS_ResetDS( gGetStmfDS : RXS_DS_TYPE_GETSTMF );
       RXS_ResetDS( gPutStmfDS : RXS_DS_TYPE_PUTSTMF );
       RXS_ResetDS( gMoveStmfDS : RXS_DS_TYPE_MOVESTMF );

       // Error handling APIs
       RXS_ResetDS( gCatchThrowErrorDS : RXS_DS_TYPE_CATCHTHROWERROR );
       
      /end-free
```


---

# RXS_StartComposeEngine()

> Initializes the RXS template compose engine, loading a compiled template (.tpl) file.

This subprocedure initializes the compose engine with a specific template file. This is utilized primarily to build XML but could be used to build any type of character data.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_StartComposeEngine...
     D                 PR                  Extproc('RXS_StartComposeEngine')
     D                                     Opdesc

      // Pass in an RXS_ComposeDS_t to control how the composition engine
      //   functions.
     D   ComposeDS                         Likeds(RXS_ComposeDS_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 6.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code initializes the composition engine for the EXAMPLE template and 
      *  configures it with the procedure address for the Template procedure.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,EXAMPLE

     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
      /free
       RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
       ComposeDS.TemplateProcedure = %Paddr(Template);
       RXS_StartComposeEngine( ComposeDS );

       *INLR = *ON;
      /end-free

     P Template        B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)
      /copy QRPGLETPL,EXAMPLE
     P                 E
```

#### Example 2

```rpgle
      *--------------------------------------------------------------
      * This example code initializes the composition engine for the EXAMPLE template and 
      *  configures it with the procedure address for the Template procedure. It also
      *  sets configuration values to trim whitespace from template lines and to
      *  omit line control characters.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,EXAMPLE

     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t) 
      /free
       RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
       ComposeDS.TemplateProcedure = %Paddr(Template);
       ComposeDS.TrimTemplateLines = RXS_YES;
       ComposeDS.OmitLineControls = RXS_YES;
       RXS_StartComposeEngine( ComposeDS );

       *INLR = *ON;
      /end-free

     P Template        B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)
      /copy QRPGLETPL,EXAMPLE
     P                 E
```

#### Example 3

```rpgle
      *--------------------------------------------------------------
      * This example code initializes the composition engine for the EXAMPLE template and 
      *  configures it with the procedure address for the Template procedure. It also
      *  sets a configuration value to skip composing lines on which there are
      *  uncomposed varaibles.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,EXAMPLE

     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t) 
      /free
       RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
       ComposeDS.TemplateProcedure = %Paddr(Template);
       ComposeDS.OmitUncomposedLines = RXS_YES;
       RXS_StartComposeEngine( ComposeDS );

       *INLR = *ON;
      /end-free

     P Template        B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)
      /copy QRPGLETPL,EXAMPLE
     P                 E
```

#### Example 4

```rpgle
      *--------------------------------------------------------------
      * This example code initializes the composition engine for the EXAMPLE template and 
      *  configures it with an IFS file containing a template file.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t) 
      /free
       RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
       ComposeDS.Stmf = '/www/RXS/templates/geturi2.tpl';
       RXS_StartComposeEngine( ComposeDS );

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_ComposeDS_t

```rpgle
     D RXS_ComposeDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     Like(RXS_ReturnedErrorInfoDS_t) Inz

      // Internal use only
     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_COMPOSE)

     D   OnErrorMessageType...
     D                                5I 0

      // Pointer to the address of the subprocedure that will compose the XML or
      //   other character based content.
     D   TemplateProcedure...
     D                                 *   Procptr

      // IFS location where a log file will be created during composition.
     D   LogFile                           Like(RXS_Var1Kv_t)

      // IFS location where a dynamically processed template file is located. If
      //   specified, the TemplateProcedure option is ignored.
     D   Stmf                              Like(RXS_Var1Kv_t)

      // Determines whether to append the data to the log file (RXS_YES), or to
      //   overwrite the log file (RXS_NO). Default: RXS_NO
     D   LogFileAppend...
     D                                 N   Inz(RXS_NO)

      // Determines whether to compose all characters in the CCSID of the Stmf
      //   (RXS_YES), or to use the CCSID of the current job (RXS_NO).
      // Default: RXS_NO
     D   UseStmfCCSID                  N

      // Determines whether to initialize the engine (RXS_YES), or to leave any
      //   previous configuration and composed content in place (RXS_NO).
      // Default: RXS_YES
     D   Initialize                    N   Inz(RXS_YES)

      // Determines whether to allow templates up to 256K in length (RXS_YES), or
      //   to use 64K (RXS_NO).
      // Default: RXS_NO
     D   LargeTemplate...
     D                                 N   Inz(RXS_NO)

      // Determines whether to strip the line controls from the end of each
      //   template line (RXS_YES), or to leave line controls in place (RXS_NO).
      // Default: RXS_NO
     D   OmitLineControls...
     D                                 N   Inz(RXS_NO)

      // Determines whether to trim leading and trailing spaces from the start
      //   and end of each template line (RXS_YES), or to leave all spaces in place
      //   (RXS_NO). Note that spaces are only trimmed from template content and
      //   not from content passed via RXS_ComposeVariable().
      // Default: RXS_NO
     D   TrimTemplateLines...
     D                                 N   Inz(RXS_NO)

      // Determines whether to omit template lines with variables that have not
      //   been composed by RXS_ComposeVariable() (RXS_YES), or to compose all
      //   template lines (RXS_NO).
      // Default: RXS_NO
     D   OmitUncomposedLines...
     D                                 N   Inz(RXS_NO)
```



---

# RXS_STR()

> Converts a numeric value to a trimmed character string.

This subprocedure should be used inside an [RXS_Parse()](https://isupport.katointegrations.com/rxs/3.3/rxs_parse.md) parsing subprocedure to convert the parsed data pointer into a character data field.

## Subprocedure Prototype

### IBM i 7.1+

```rpgle
      // Returns the character data field extracted from the Pointer parameter.
     D RXS_STR         PR                  Extproc('RXS_STR') Opdesc
     D                                     Like(RXS_Var16Mv_t) Rtnparm

      // Pointer containing character data parsed using an RXS_Parse() parsing
      //   subprocedure.
     D   Pointer                       *   Const

      // Length of the parsed character data contained in the Pointer parameter.
     D   PointerLen                  10I 0 Const
```


### IBM i 6.1

```rpgle
     D RXS_STR         PR                  Extproc('RXS_STR') Opdesc

      // The character data field extracted from the Pointer parameter.
     D   String                            Like(RXS_Var16Mv_t) Options(*Varsize)

      // Pointer containing character data parsed using an RXS_Parse() parsing
      //   subprocedure.
     D   Pointer                       *   Const

      // Length of the parsed character data contained in the Pointer parameter.
     D   PointerLen                  10I 0 Const
```


## Example Code

### IBM i 7.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code converts the contents of the pData pointer to character
      *  data and stores it in the field gCelcius.
      * This process may be used in a RXS_Parse() parsing subprocedure to convert data
      *  retrieved from an XML element to character data for further processing.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gCelcius        S              5A   Varying
     D pData                           *   Value
     D pDataLen                      10I 0 Value
      /free
       gCelsius = RXS_STR( pData : pDataLen );

       *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code converts the contents of the pData pointer to character
      *  data and stores it in the field gCelcius.
      * This process may be used in a RXS_Parse() parsing subprocedure to convert data
      *  retrieved from an XML element to character data for further processing.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gCelcius        S              5A   Varying
     D pData                           *   Value
     D pDataLen                      10I 0 Value
      /free
       RXS_STR( gCelcius : pData : pDataLen );

       *INLR = *ON;
      /end-free
```


---

# RXS_Throw()

> Throws an RXS exception with a message and code, to be caught by RXS_Catch().

This subprocedure can be used to throw an error. The error information is populated into a data structure which is then passed to RXS_Throw. The error can be caught by [RXS_Catch()](https://isupport.katointegrations.com/rxs/3.3/rxs_catch.md).

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_Throw...
     D                 PR                  Extproc('RXS_Throw')

      // The RXS_CatchThrowErrorDS_t data structure that contains the error
      //   information to be thrown.
     D   DS                                Likeds(RXS_CatchThrowErrorDS_t)
```


## Example Code

### IBM i 6.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code demonstrates throwing a diagnostic error
      *  by assigning values to a RXS_CatchThrowErrorDS_t datastructure and
      *  throwing the error datastructure to the calling program.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gErrorDS        DS                  Likeds(RXS_CatchThrowErrorDS_t)
     D                                     Inz(*LikeDS)
      /free
       // Send a diagnostic error message

       RXS_ResetDS( gErrorDS : RXS_DS_TYPE_CATCHTHROWERROR );
       gErrorDS.MessageId = 'CPF9898';                                       
       gErrorDS.MessageFile = 'QCPFMSG';                                     
       gErrorDS.MessageData = 'This is a *DIAG error being thrown'; 
       gErrorDS.MessageType = RXS_MESSAGE_TYPE_DIAG;                         
       RXS_Throw(gErrorDS);

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_CatchThrowErrorDS_t

```rpgle
     D RXS_CatchThrowErrorDS_t...
     D                 DS                  Qualified Template Inz

      // The message ID that was received.
     D   MessageId                    7A

      // The message file used to store the message ID.
     D   MessageFile                 20A

      // The message data to be merged with the text of the message ID.
     D   MessageData               1024A   Varying

     D   DataStructureType...
     D                                5I 0 Inz(RXS_DS_TYPE_CATCHTHROWERROR)

      // The type of message that was sent.
      // Valid values: RXS_MESSAGE_TYPE_DIAG, RXS_MESSAGE_TYPE_COMP,
      //   RXS_MESSAGE_TYPE_INFO, RXS_MESSAGE_TYPE_INQ, RXS_MESSAGE_TYPE_RQS,
      //   RXS_MESSAGE_TYPE_NOTIFY, RXS_MESSAGE_TYPE_ESCAPE,
      //   RXS_MESSAGE_TYPE_STATUS
     D   MessageType                  5I 0

      // The full message text that combines the value of the message ID's text
      //   and the merged message data.
     D   MessageText               4096A   Varying

      // Set to RXS_YES when you want the error sent to the program that called
      //   the program which is throwing the error. By default, the program
      //   throwing the error will be the target of the error.
     D   ThrowToCaller...
     D                                 N   Inz(RXS_NO)
```



---

# RXS_Transmit()

> Performs HTTP/HTTPS requests with configurable headers, authentication, encoding, and timeout options.

Performs a HTTP/HTTPS request - allowing you to exchange data with a remote web service.

To properly communicate with a web service, you will need to configure the RXS_TransmitDS_t data structure passed in as a parameter.

## Subprocedure Prototype

### V7R1+

```rpgle
      // Returns the response data. Note that if RXS_TransmitDS_t.ResponseStmf is
      //   set to an IFS path, the response will be stored there instead.
     D RXS_Transmit...
     D                 PR                  Extproc('RXS_Transmit') Opdesc
     D                                     Like(RXS_Var16Mv_t)
     D                                     Rtnparm

      // Holds the request data. Note that if RXS_TransmitDS_t.RequestStmf is set
      //   to an IFS path, the request will be read from there instead.
     D  Request                            Like(RXS_Var16Mv_t) Const
     D                                     Options(*Omit : *Varsize)

      // Controls how RXS_Transmit performs the HTTP/HTTPs request. Uses
      //   RXS_TransmitDS_t as a template.
     D  DS                                 LikeDS(RXS_TransmitDS_t)
```


### V6R1

```rpgle
     D RXS_Transmit...
     D                 PR                  Extproc('RXS_Transmit') Opdesc

      // Holds the response data. Note that if RXS_TransmitDS_t.ResponseStmf is
      //   set to an IFS path, the response will be stored there instead.
     D  Response                           Like(RXS_Var16Mv_t)
     D                                     Options(*Omit : *Varsize)

      // Holds the request data. Note that if RXS_TransmitDS_t.RequestStmf is set
      //   to an IFS path, the request will be read from there instead.
     D  Request                            Like(RXS_Var16Mv_t) Const
     D                                     Options(*Omit : *Varsize)

      // Controls how RXS_Transmit performs the HTTP/HTTPs request. Uses
      //   RXS_TransmitDS_t as a template.
     D  DS                                 LikeDS(RXS_TransmitDS_t)
```


## Example Code

### IBM i 7.1+

#### HTTP GET

```rpgle
      *--------------------------------------------------------------
      * This example performs a simple HTTP GET operation against the URL example.com and stores the retrieved response data in the field Response.
      * Because this is a HTTP GET, we don't need to pass a Request parameter.
      * During the execution of RXS_Transmit(), the API will create a log file at '/tmp/rxs_transmit1_log.txt'
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        TransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
        TransmitDS.LogFile = '/tmp/rxs_transmit1_log.txt';
        Response = RXS_Transmit( *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP POST

```rpgle
      *--------------------------------------------------------------
      * This example performs a HTTP POST against a public web service that provides conversion between Fahrenheit and Celsius.
      * This is a HTTP POST, so we need to provide a Request parameter.
      * Because this is using SOAP, we need to specify Content-type and SOAPAction headers.
      * Lastly, this example uses RXS_JobLog() to write the full response XML to the job log.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Request         S                   Like(RXS_Var64Kv_t)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        Request = '<?xml version="1.0" encoding="utf-8"?>' +
         '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'+
         ' xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="' +
         'http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>' +
         '<FahrenheitToCelsius xmlns="http://www.w3schools.com/webservices/">'+
         '<Fahrenheit>100</Fahrenheit></FahrenheitToCelsius>' +
         '</soap:Body></soap:Envelope>';

        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI =
          'http://www.w3schools.com/webservices/tempconvert.asmx';
        TransmitDS.HTTPMethod = RXS_HTTP_METHOD_POST;
        TransmitDS.LogFile = '/tmp/rxs_transmit2_log.txt';
        TransmitDS.HeaderContentType = 'text/xml; charset=utf-8';
        TransmitDS.HeaderSOAPAction =
          '"http://www.w3schools.com/webservices/FahrenheitToCelsius"';
        Response = RXS_Transmit( Request : TransmitDS );

        RXS_JobLog( Response );

        *INLR = *ON;
      /end-free
```

#### HTTP Custom Headers

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates how to set custom HTTP headers with RXS_Transmit(). 
      * You may specify up to 50 custom HTTP headers sent with a request.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        // Set a custom HTTP header that looks like this:
        // X-API-Token: [your-api-token]
        TransmitDS.CustomHeaderName(1) = 'X-API-Token';
        TransmitDS.CustomHeaderValue(1) = '[your-api-token]'; 
        // Set a custom HTTP header that looks like this:
        // ExampleHeader: hello world
        TransmitDS.CustomHeaderName(2) = 'ExampleHeader';  
        TransmitDS.CustomHeaderValue(2) = 'hello world';    
        Response = RXS_Transmit( *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Basic Authentication

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates how to specify a username and
      * password for HTTP Basic Authentication with RXS_Transmit().
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        TransmitDS.BasicAuthUser = 'username';
        TransmitDS.BasicAuthPassword = 'password';    
        Response = RXS_Transmit( *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Bearer Authentication

```rpgle
      *--------------------------------------------------------------
      * This examples showcases using RXS_Transmit to set an Authorization header. 
      * When using CustomHeaderName and CustomHeaderValue arrays with RXS_Transmit()
      * to set an Authorization header, the Bearer portion needs to go in the Value 
      * field, followed by a space before the API key is concatenated to the Value field. 
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        TransmitDS.CustomHeaderName(1) = 'Authorization';
        TransmitDS.CustomHeaderValue(1) = 'Bearer ' + '[your-api-token]'; 
        Response = RXS_Transmit( *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Cookie Header Data

```rpgle
      *--------------------------------------------------------------
      * This example showcases sending cookie data. However, instead of 
      * reading the data from a file, you may specify it by utilizing
      * HeaderCookieData.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'example.com';
        TransmitDS.HeaderCookieData = 'your cookie data';
        Response = RXS_Transmit( *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Cookie Header File

```rpgle
      *--------------------------------------------------------------
      * This example showcases sending cookie data, utilizing an IFS
      * file which contains said data.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'example.com';
        TransmitDS.HeaderCookieFiles(1) = '/tmp/cookie.txt';
        Response = RXS_Transmit( *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

### IBM i 6.1

#### HTTP GET

```rpgle
      *--------------------------------------------------------------
      * This example performs a simple HTTP GET operation against the URL example.com and stores the retrieved response data in the field Response.
      * Because this is a HTTP GET, we don't need to pass a Request parameter.
      * During the execution of RXS_Transmit(), the API will create a log file at '/tmp/rxs_transmit1_log.txt'
      * Note that the compiler directive RXSV6R1 is defined. This ensures that if you upgrade your 6.1 system 
      * to 7.1 at a later date that you will not need to make changes to this program to be able to compile 
      * it again due to the structural differences between how RXS APIs function on 6.1 vs 7.1 / 7.2
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        TransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
        TransmitDS.LogFile = '/tmp/rxs_transmit1_log.txt';
        RXS_Transmit( Response : *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP POST

```rpgle
      *--------------------------------------------------------------
      * This example performs a HTTP POST against a public web service that provides conversion between Fahrenheit and Celsius.
      * This is a HTTP POST, so we need to provide a Request parameter.
      * Because this is using SOAP, we need to specify Content-type and SOAPAction headers.
      * Lastly, this example uses RXS_JobLog() to write the full response XML to the job log.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

     /define RXSV6R1
     /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Request         S                   Like(RXS_Var64Kv_t)
     D Response        S                   Like(RXS_Var64Kv_t)
     /free
      Request = '<?xml version="1.0" encoding="utf-8"?>' +
       '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'+
       ' xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="' +
       'http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>' +
       '<FahrenheitToCelsius xmlns="http://www.w3schools.com/webservices/">'+
       '<Fahrenheit>100</Fahrenheit></FahrenheitToCelsius>' +
       '</soap:Body></soap:Envelope>';

      RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
      TransmitDS.URI =
        'http://www.w3schools.com/webservices/tempconvert.asmx';
      TransmitDS.HTTPMethod = RXS_HTTP_METHOD_POST;
      TransmitDS.LogFile = '/tmp/rxs_transmit2_log.txt';
      TransmitDS.HeaderContentType = 'text/xml; charset=utf-8';
      TransmitDS.HeaderSOAPAction =
        '"http://www.w3schools.com/webservices/FahrenheitToCelsius"';
      RXS_Transmit( Response : Request : TransmitDS );

      RXS_JobLog( Response );

      *INLR = *ON;
     /end-free
```

#### HTTP Custom Headers

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates how to set custom HTTP headers with RXS_Transmit(). 
      * You may specify up to 50 custom HTTP headers sent with a request.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        // Set a custom HTTP header that looks like this:
        // X-API-Token: [your-api-token]
        TransmitDS.CustomHeaderName(1) = 'X-API-Token';
        TransmitDS.CustomHeaderValue(1) = '[your-api-token]'; 
        // Set a custom HTTP header that looks like this:
        // ExampleHeader: hello world
        TransmitDS.CustomHeaderName(2) = 'ExampleHeader';  
        TransmitDS.CustomHeaderValue(2) = 'hello world';     
        RXS_Transmit( Response : *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Basic Authentication

```rpgle
      *--------------------------------------------------------------
      * This example demonstrates how to specify a username and
      * password for HTTP Basic Authentication with RXS_Transmit().
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        TransmitDS.BasicAuthUser = 'username';
        TransmitDS.BasicAuthPassword = 'password';    
        RXS_Transmit( Response : *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Bearer Authentication

```rpgle
      *--------------------------------------------------------------
      * This examples showcases using RXS_Transmit to set an Authorization header. 
      * When using CustomHeaderName and CustomHeaderValue arrays with RXS_Transmit()
      * to set an Authorization header, the Bearer portion needs to go in the Value 
      * field, followed by a space before the API key is concatenated to the Value field. 
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'https://example.com/';
        TransmitDS.CustomHeaderName(1) = 'Authorization';
        TransmitDS.CustomHeaderValue(1) = 'Bearer ' + '[your-api-token]'; 
        RXS_Transmit( Response : *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Cookie Header Data

```rpgle
      *--------------------------------------------------------------
      * This example showcases sending cookie data. However, instead of 
      * reading the data from a file, you may specify it by utilizing
      * HeaderCookieData.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'example.com';
        TransmitDS.HeaderCookieData = 'your cookie data';
        RXS_Transmit( Response : *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

#### HTTP Cookie Header File

```rpgle
      *--------------------------------------------------------------
      * This example showcases sending cookie data, utilizing an IFS
      * file which contains said data.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /define RXSV6R1
      /copy QRPGLECPY,RXSCB

     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D                                     Inz(*LikeDS)
     D Response        S                   Like(RXS_Var64Kv_t)
      /free
        RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
        TransmitDS.URI = 'example.com';
        TransmitDS.HeaderCookieFiles(1) = '/tmp/cookie.txt';
        RXS_Transmit( Response : *Omit : TransmitDS );

        *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_TransmitDS_t

```rpgle
     D RXS_TransmitDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // Specify the full URI used for the request. If a non-standard port (e.g.
      //   other than 80 for an HTTP request, and 443 for an HTTPS request) is
      //   needed, this must be specified in the request.
      // Example: https://testuri.org:9001/webservice
     D   URI                               Like(RXS_Var8Kv_t)

      // An IFS path can be specified in this subfield - if it is, it will be
      //   used as the request data instead of the value of the pRequest parm.
     D   RequestStmf                       Like(RXS_Var1Kv_t)

      // Specify the CCSID of the data to be sent to the remote server. If
      //   required, the data in the Request parameter will be converted to this
      //   CCSID before transmission to the remote server.
      // Default: RXS_CCSID_UTF8
     D   RequestCcsid                10I 0 Inz(RXS_CCSID_UTF8)

      // An IFS path can be specified in this subfield - if it is, any output of
      //   the RXS_Transmit operation will be stored in the specified IFS file.
     D   ResponseStmf                      Like(RXS_Var1Kv_t)

      // If the response is being stored in an IFS stream file (determined by
      //   whether or not the ResponseStmf subfield is occupied), the response data
      //   will be converted to this CCSID. The stream file will also be created
      //   with this CCSID.
      // Default: RXS_CCSID_UTF8
     D   ResponseStmfCcsid...
     D                               10I 0 Inz(RXS_CCSID_UTF8)

      // Specify a value in seconds for a timeout on the request.
      // Default: 60
     D   Timeout                     10U 0 Inz(60)

      // Determines the HTTP request method used.
      // Valid values: RXS_HTTP_METHOD_GET, RXS_HTTP_METHOD_POST,
      //   RXS_HTTP_METHOD_PUT, RXS_HTTP_METHOD_DELETE, RXS_HTTP_METHOD_PATCH,
      //   RXS_HTTP_METHOD_HEAD, RXS_HTTP_METHOD_OPTIONS
      // Default: RXS_HTTP_METHOD_POST
     D   HTTPMethod                   3I 0 Inz(RXS_HTTP_METHOD_POST)

      // An IFS path can be specified to log the request & response, as well as
      //   additional HTTP request debugging information. This option is disabled
      //   by default, and should not be left enabled in production environments
      //   due to increased execution time and privacy concerns.
     D   LogFile                           Like(RXS_Var1Kv_t)

      // Specify a value in seconds to cause a timeout during an SSL handshake.
      // Default: 300
     D   SSLTimeout                  10U 0 Inz(300)

      // Used to assign an application ID for RXS_Transmit.
     D   SSLApplicationID...
     D                                     Like(RXS_Var1Kv_t)

      // If a SSL certificate store other than the *SYSTEM store should be used,
      //   the IFS path can be specified here.
     D   SSLCertStore...
     D                                     Like(RXS_Var1Kv_t)

      // If using a non-*SYSTEM SSL certificate store, the password can be
      //   specified here.
     D   SSLCertStorePassword...
     D                                     Like(RXS_Var1Kv_t)

      // This option determines whether RXS_Transmit verifies the authenticity of
      //   the peer's certificate. Setting this to RXS_NO this in a production
      //   capacity is strongly discouraged.
      // Valid values: RXS_YES, RXS_NO
      // Default: RXS_YES
     D   SSLVerifyPeer...
     D                                 N   Inz(RXS_YES)

      // This option determines whether RXS_Transmit verifies that the server
      //   certificate is for the server it is known as. Setting this to RXS_NO
      //   this in a production capacity is strongly discouraged.
      // Valid values: RXS_YES, RXS_NO
      // Default: RXS_YES
     D   SSLVerifyHost...
     D                                 N   Inz(RXS_YES)

      // Specify the HTTP protocol version being used.
      // Valid values: RXS_HTTP_VERSION_ANY, RXS_HTTP_VERSION_11,
      //   RXS_HTTP_VERSION_10
      // Default: RXS_HTTP_VERSION_ANY
     D   HTTPVersion                  4P 2 Inz(RXS_HTTP_VERSION_ANY)

      // Specify the user ID for this parameter if Basic Authentication is used
      //   on this request.
     D   BasicAuthUser...
     D                                     Like(RXS_Var1Kv_t)

      // Specify the password for this parameter if Basic Authentication is used
      //   on this request.
     D   BasicAuthPassword...
     D                                     Like(RXS_Var1Kv_t)

      // Specifies the URI used as a proxy for the request.
     D   ProxyURI                          Like(RXS_Var1Kv_t)

      // If using a proxy on this request that requires a user id and password,
      //   specify the proxy user id in this subfield.
     D   ProxyUser...
     D                                     Like(RXS_Var1Kv_t)

      // If using a proxy on this request that requires a user id and password,
      //   specify the proxy password in this subfield.
     D   ProxyPassword...
     D                                     Like(RXS_Var1Kv_t)

      // Specifies if HTTP headers should be sent with the request. RXS_YES will
      //   tell RXS_Transmit that all HTTP headers as well as the user defined
      //   headers are sent with the request. RXS_NO will tell RXS_Transmit that no
      //   HTTP headers are sent with the request - only the request data is sent.
      // Valid values: RXS_YES, RXS_NO
      // Default: RXS_YES
     D   SendHTTPHeaders...
     D                                 N   Inz(RXS_YES)

      // Specify any cookie data to be sent with this request. Further
      //   instructions for formatting this data is available at RFC2109.
     D   HeaderCookieData...
     D                                     Like(RXS_Var8Kv_t)

      // Up to 50 paths to IFS files may be specified which contain cookie data.
      //   Further instructions for formatting this data is available at RFC2109.
     D   HeaderCookieFiles...
     D                                     Like(RXS_Var1Kv_t) Dim(50)

      // If calling a SOAP web service, the SOAPAction HTTP header field is
      //   generally required. This is typically surrounded by double quotes.
      // Example: "http://electrocommerce.org/abc#MyMessage"
     D   HeaderSOAPAction...
     D                                     Like(RXS_Var1Kv_t)

      // Some web applications may function differently depending upon the user
      //   agent specified.
      // Default: KrengelTech HTTP Client
     D   HeaderUserAgent...
     D                                     Like(RXS_Var1Kv_t)
     D                                     Inz('KrengelTech HTTP Client')

      // Populates the HTTP Accept header, used to specify which data type(s)
      //   this service will accept in a response.
     D   HeaderAccept                      Like(RXS_Var1Kv_t)

      // Populates the HTTP Host header, which specifies the host address and
      //   port number of the requested resource. This information is generally
      //   obtained from the service URI. This field is required for HTTP/1.1
      //   communications.
     D   HeaderHost                        Like(RXS_Var1Kv_t)

      // Sets the HTTP Referer header, which can be used to specify a URI from
      //   which this service address was obtained.
     D   HeaderReferer...
     D                                     Like(RXS_Var1Kv_t)

      // Sets the HTTP Connection header, which is used to specify
      //   connection-specific options.
     D   HeaderConnection...
     D                                     Like(RXS_Var1Kv_t)

      // The MIME type of the body of the request (used with POST requests).
      // Default: text/xml
     D   HeaderContentType...
     D                                     Like(RXS_Var1Kv_t)

      // Specifies the name of up to 50 custom HTTP headers sent with the
      //   request.
     D   CustomHeaderName...
     D                               64A   Varying Dim(50)

      // Specifies the value of up to 50 custom HTTP headers sent with the
      //   request.
     D   CustomHeaderValue...
     D                                     Like(RXS_Var1Kv_t) Dim(50)

      // Data structure using RXS_HTTPResponseDS_t as a template. Populated by
      //   RXS_Transmit at the end of the request.
     D   HTTPResponse                      LikeDS(RXS_HTTPResponseDS_t)
     D                                     Inz(*LikeDS)

      // Internal use only
     D   InputPointer...
     D                                 *

      // Internal use only
     D   InputLength                 10I 0

      // Internal use only
     D   OutputPointer...
     D                                 *

      // Internal use only
     D   OutputLength                10I 0

      // Internal use only
     D   Reserved                  4096A

      // The CCSID of the data supplied in the Request parameter of
      //   RXS_Transmit(). If required, the data will be converted from the
      //   InputCcsid into the RequestCcsid before being transmitted.
     D   InputCCSID                  10I 0 Inz(RXS_CCSID_JOB)

      // The CCSID of the data to be returned in the Response parameter of
      //   RXS_Transmit().
     D   OutputCCSID                 10I 0 Inz(RXS_CCSID_JOB)

     D   LogFileCCSID                10I 0 Inz(RXS_CCSID_UTF8)

     D   LogFileAppend...
     D                                 N   Inz(RXS_NO)

     D   LogHandler...
     D                                 *   Procptr

     D   LogHandlerData...
     D                                 *

     D   LogHandlerDataLength...
     D                               10I 0

     D   EnableExpectHeader...
     D                                 N   Inz(RXS_NO)

      // Specify the IP address and optionally the port of the desired interface
      //   to bind to when transmiting to the remote server. When specify port, it
      //   will be separated from the IP with a colon: IP:Port
     D   LocalInterface...
     D                               64A   Varying
```



---

# RXS_Validate()

> Validates an XML document against an XSD schema, returning validation errors if the document does not conform.

This subprocedure performs validation options on a given character string.

Currently it can perform XSD validation against XML.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
     D RXS_Validate...
     D                 PR              N   Extproc('RXS_Validate') Opdesc

      // Character data to be validated.
     D  pData                              Like(RXS_Var16Mv_t)
     D                                     Options(*Omit: *Varsize) Const

      // This field can contain either an RXS_XsdValidationDS_t data structure,
      //   or the XSD stored in a character field.
     D  pDS                                Like(RXS_Var64K_t)
     D                                     Options(*Varsize)
```


## Example Code

### IBM i 6.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code validates XML data against a specified XSD.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D XsdValidateDS...
     D                 DS                  LikeDS(RXS_XsdValidationDS_t)
     D                                     Inz(*LikeDS)
     D goodXml         S                   Like(RXS_Var64Kv_t)
      /free
       // This example assumes that goodXml contains XML data.
       //  This data could be retrieved from RXS_GetStdIn(), or composed
       //  using the RXS composition engine.

       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdLocation =
       'http://files.rpg-xml.com/example/xsdvalidate/validate.xsd';
       if RXS_Validate( goodXml : XsdValidateDS );
         RXS_JobLog( 'XML 1 Validated successfully');
       else;
         RXS_JobLog( 'XML 1 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s' :
           %Char( XsdValidateDS.ValidationErrorCode ) :
           XsdValidateDS.ValidationErrorText );
       endif;

       *INLR = *ON;
      /end-free
```

## Data Structures

### RXS_XsdValidationDS_t

```rpgle
     D RXS_XsdValidationDS_t...
     D                 DS                  Qualified Template Inz

     D   ReturnedErrorInfo...
     D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz

     D   OnErrorMessageType...
     D                               10I 0

      // The IFS path or URI where the XSD is located.
     D   XsdLocation...
     D                                     Like(RXS_Var1Kv_t)

      // Error code returned by the validation operation.
     D   ValidationErrorCode...
     D                               10I 0

      // Error message returned by the validation operation.
     D   ValidationErrorText...
     D                                     Like(RXS_Var1Kv_t)

      // CCSID of the XSD data.
     D   XsdCcsid                    10I 0

      // Internal use only
     D   XsdPointer                    *

      // Internal use only
     D   XsdLength                   10I 0

      // The CCSID of the XML data.
     D   XmlCcsid                    10I 0

      // Internal use only
     D   XmlPointer                    *

      // Internal use only
     D   XmlLength                   10I 0

      // Internal use only
     D   Reserved                  4096A
```



---

# RXS_XPath()

> Evaluates an XPath expression against a DOM and returns the matching node value as a string.

This subprocedure assists with building an XPath string. This is recommended over simply manually building an XPath string.

In addition to providing an easy way to include numeric replacement values, it also allows for including a wildcard namespace. The wildcard namespace syntax is translated to its' XPath 1.0 equivalent and returned along with any replacements that were passed.

## Subprocedure Prototype

### IBM i 6.1+

```rpgle
      // Returns the constructed XPath string.
     D RXS_XPath...
     D                 PR                  Extproc('RXS_XPath') Opdesc
     D                                     Like(RXS_Var8Kv_t)
     D                                     Rtnparm

      // Holds the initial XPath string template
     D  pXPath                             Like(RXS_Var8Kv_t) Const
     D                                     Options(*Varsize)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement1                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement2                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement3                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement4                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement5                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement6                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement7                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement8                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement9                10U 0 Value Options(*Nopass)

      // Holds a numeric replacement value. Replaces the first unreplaced %u
      //   found in the XPath string template.
     D  pReplacement10...
     D                               10U 0 Value Options(*Nopass)
```


## Example Code

### IBM i 6.1+

#### Example 1

```rpgle
      *--------------------------------------------------------------
      * This example code demonstrates creating an XPath that can be used for
      *  further processing.
      *--------------------------------------------------------------
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB

     D gXPath          S                   Like(RXS_Var1Kv_t)
      /free
       gXPath = RXS_XPath( '/*:bookstore/*:book' );

       *INLR = *ON;
      /end-free
```


---

# Apply License (APYLIC)

> Applies a permanent or temporary license key to the RPG API Express installation.

To apply a permanent or temporary license key, you will use the included command APYLIC. Your license key will typically be supplied via email in a command string:

`APYLIC LICKEY(6E2222898982C8F1380F4DF08A23922C) PRODUCT(RXS)`

Or, you can copy and paste the received key into the command:

![Apply License (APYLIC)](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/apylic.png "Apply License (APYLIC)")

In either case after running the APYLIC command, you should receive a message that says **License applied successfully**

If you don't receive that message or receive an error message, please contact us at <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

---

# Display License (DSPLIC)

> Legacy alias for DSPMCHINF; displays machine and license information needed for activation key generation.

DSPLIC is used to retrieve system information as well as license status.

**Note:** [Display Machine Info (DSPMCHINF)](https://isupport.katointegrations.com/rxs/3.3/dspmchinf.md) is an alias for this command. These commands may be used interchangeably.

The command does not have any parameters:

`DSPLIC`

The command should display a screen similar to the one below. This screen indicates all the information necessary to generate your license key, as well as the current status of any applied license keys.

![Display License (DSPLIC)](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/dsplic.png "Display License (DSPLIC)")

If you don't receive that message or receive an error message, please contact us at <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

---

# Display Machine Info (DSPMCHINF)

> Displays system machine information and license key status required by support to generate activation keys.

DSPMCHINF is used to retrieve system information as well as license status.

**Note:** DSPMCHINF is an alias for the [Display License (DSPLIC)](https://isupport.katointegrations.com/rxs/3.3/dsplic.md) command. These commands may be used interchangeably.

The command does not have any parameters:

`DSPMCHINF`

The command should display a screen similar to the one below. This screen indicates all the information necessary to generate your license key, as well as the current status of any applied license keys.

![Display Machine Info (DSPMCHINF)](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/dspmchinf.png "Display Machine Info (DSPMCHINF)")

If you don't receive that message or receive an error message, please contact us at <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

---

# Display Version (DSPVER)

> Displays the currently installed version of RPG API Express.

DSPVER can be used to retrieve the version number of the currently installed product.

The command does not have any parameters:

`DSPVER`

You should receive a message indicating the currently installed version as well as the library in which the product is installed.

![Display Version (DSPVER)](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/dspver.png "Display Version (DSPVER)")

If you don't receive that message or receive an error message, please contact us at <a href="mailto:isupport@katointegrations.com">isupport@katointegrations.com</a>.

---

# RXSMENU

> Menu interface for viewing RXS installation details and managing product license keys.

The RXSMENU menu can be used to retrieve information about your RPG-XML Suite installation, or to work with your product license keys.

![RXSMENU](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/rxsmenu.png)

---

# Create RXS Server (CRTRXSSVR)

> Creates an Apache HTTP server instance configured for use with RPG API Express.

The Create RXS HTTP Server (CRTRXSSVR) command allows the user to create an HTTP server instance for use by RPG-XML Suite.

## Parameters

### HTTP server instance (HTTPSVR)
This is a required parameter.

Specifies the name of the HTTP server instance to create.

If this parameter is specified with any value other than HTTPSVR(*RXSLIB), an RPG-XML Suite development library is also created, with the same name as the HTTP server instance. Several objects are copied into it from the RPG-XML Suite product library. It is these objects (files, programs service programs, etc.) that are used when you develop your applications which use RPG-XML Suite.

If you specify HTTPSVR(*RXSLIB), only the HTTP server instance is created with the same name as the RPG-XML Suite product library. You should use this option if you only wish to use the RXS Router product, and are not intending to do any other RPG-XML Suite development.

#### Possible Values:
`name`
: An HTTP server instance is created and an RPG-XML Suite development environment library is also created with the same name.

`RXSLIB`
: An HTTP server instance is created with the same name as the RPG-XML Suite product library. No RPG-XML Suite development environment library is created.

### RXS product library (RXSLIB)
Specifies the name of the RPG-XML Suite product library to use when creating the RPG-XML Suite environment.

#### Possible Values:
`RXS`
: The default RPG-XML Suite product library RXS will be used.

`library-name`
: The named RPG-XML Suite product library will be used.

### Port number (PORT)
Specifies the port number to use for the HTTP server instance.

**Note: No checks are performed that the specified port number is not already in use, either by an existing HTTP server instance or by another application.**

#### Possible Values:
`DFT`
: The default port number 8181 is used.

`port-number`
: The specified port number is used.

---

# Process HTTP Server Instance (HTTP)

> Manages Apache HTTP server instances with options to start, end, restart, view logs, inspect configuration, and manage autostart.

The barebones approach to ending an Apache HTTP server is to use the ENDTCPSVR command. The ENDTCPSVR command has a default of *ALL for the SERVER parameter which can obviously shut down all TCP related services, making you very unpopular. For this reason we have created a command that can help you with your Apache HTTP server needs. The command is named "HTTP". Simply type HTTP on the command line and select F4 to see the various options.

## Parameters

### HTTP Server Instance (HTTPSVR)
Specify the Apache HTTP server instance to perform actions upon.

#### Possible Values:

`*ALL`
: Work with all HTTP instances. The only valid values for the Option parameter that can be used with this is *WRK.

`*ADMIN`
: Work with all *ADMIN HTTP instances. The only valid values for the Option parameter that can be used with this are *WRK, *START, *RESTART, and *END

`name`
: Specify the name of a specific HTTP instance to work with. All values are valid for the Option parameter.

`generic-name`
: Specify the generic name of the HTTP server instance to be processed. A generic name is a character string that contains one or more characters followed by an asterisk (*). All values are valid for the Option parameter.

### Option (OPTION)
Specify the operation which should be performed on the specified HTTP server instance(s).

#### Possible Values:

`*WRK`
: Works with the specified HTTP server instance(s). This displays the Work with Active Jobs (WRKACTJOB) screen, subsetted to show only the selected HTTP server instance(s).

`*START`
: Starts the specified HTTP server instance.

`*END`
: Ends the specified HTTP server instance.

`*INZRTR`
: Intializes the RXSRTR cache.

`*RESTART`
: Restarts the specified HTTP server instance.

`*CONF`
: Edits the configuration file for the specified HTTP server instance. If the user does not have sufficient authority to edit the configuration file, it will be displayed.

`*LOGS`
: Displays a list of the the error logs for the specified HTTP server instance.

`*CURLOG`
: Displays the current day's error log for the specified HTTP server instance.

`*DOCS`
: Displays the contents of the '/htdocs/' folder for the specified HTTP server instance.

`*DIR`
: Displays the entire directory structure for the specified HTTP server instance.

---

# RXS Router (RXSRTR)

> CGI routing program that dispatches HTTP requests to RPG programs, supporting library lists, user profile swapping, audit logging, and ASP group selection.

The RXS Router (RXSRTR) is a program included since RPG-XML Suite v2.10+ that runs on the IBM i behind the Apache HTTP server, acting as a 'router' for CGI requests which use the HTTP protocol.

RXSRTR enables you to both simplify your HTTP processing and add functionality which is not directly available through the HTTP server configuration. Once installed, RXSRTR sits seamlessly between your HTTP server and your CGI programs; no changes are required to existing CGI program functionality and no changes are required to any existing URLs you use to access your CGI programs (such as user favorites or links from webpages).

RXSRTR enables you to perform the following additional functionality:

- Route all CGI requests through a single URL format
- Specify a library list to use when calling programs
- Swap to a different user profile when calling programs
- Log all information about the request to an audit log file
- Call non-CGI programs, passing parameters and receiving responses
- Specify an activation group allowing files to remain open between calls, while maintaining separation between environments
- Specify product libraries which will be placed ahead of libraries in the user library list

All this functionality is data-driven - that is, it is controlled via data held in a single control file used by RXSRTR. If you need to add or change the programs you need to call, you simply update the RXSRTR control file.

## HTTP Configuration Without RXSRTR

Below is an example HTTP server instance configuration fragment, showing how three different URL formats match different CGI program calls in different libraries:

```apache
ScriptAliasMatch ^/cgi-bin/(.*) /qsys.lib/public.lib/$1.pgm
ScriptAliasMatch ^/cust/(.*) /qsys.lib/customers.lib/initial.pgm
ScriptAliasMatch ^/db2wse/(.*) /qsys.lib/db2wse.lib/db2wse.pgm
<Directory /qsys.lib/public.lib/>
  Options +ExecCGI
  Allow From all
</Directory>
<Directory /qsys.lib/customers.lib/>
  Options +ExecCGI
  Allow From all
</Directory>
<Directory /qsys.lib/db2wse.lib/>
  Options +ExecCGI
  Allow From all
</Directory>
```

In this case, multiple ScriptAliasMatch directives route different URLs to different CGI programs in different libraries. For instance, assuming this HTTP server is on a IBM i with an IP address of 192.168.0.1, below is a list of example URLs and the resulting program which will be called:

| URL used | CGI Program Called |
|----------|---|
| `http://192.168.0.1/cgi-bin/mypgm` | PUBLIC/MYPGM |
| `http://192.168.0.1/cgi-bin/yourpgm` | PUBLIC/MYPGM |
| `http://192.168.0.1/cust/john` | CUSTOMERS/INITIAL |
| `http://192.168.0.1/cust/` | CUSTOMERS/INITIAL |
| `http://192.168.0.1/db2wse/string` | DB2WSE/DB2WSE |


This can be confusing, since it's not always clear which program will eventually be called when a given URL is used and the configuration can grow very large and complex, since each library specified has to have its own <Directory> section in the configuration, specifying authorities etc. Additionally, if any changes are required, the HTTP server must be restarted. Finally, the above configuration is also insecure, since it allows any user to call any program in the PUBLIC library.

## HTTP Configuration With RXSRTR

By contrast, RXSRTR allows you to replace all the above configuration with a single ScriptAliasMatch and <Directory> section in your HTTP server instance configuration file which can be used for all CGI program calls:

```apache
ScriptAliasMatch ^/(([^/.]+/)*[^/.]+)$ /qsys.lib/RXS.lib/rxsrtr.pgm
<Directory /qsys.lib/RXS.lib/>
  Options +ExecCGI
  Allow From all
</Directory>
```

Using the above directive, every URL will call RXSRTR and by configuring RXSRTR, you can ensure that all existing URLs (which may exist as links in web pages, user's bookmarks etc.) will result in the same programs being called as before.

Within the RXSRTR configuration itself, you define which programs should be called and how. You can dynamically change these configuration settings without needing to change the HTTP server configuration or restart the HTTP server.

This means that you can have a much simpler HTTP configuration. Because RXSRTR works at the program level, you can easily configure RXSRTR to specify exactly which programs can be called, immediately making your system more secure.

When a request is matched by the HTTP server which results in RXSRTR being called, RXSRTR analyzes the URL to determine a 'request routing identifier' (RRID), which will be used to determine the routing call processing for the request. This processing is described in detail in the [RXS Router Configuration](#rxs-router-configuration) section.

Once the RRID has been determined for a request, RXSRTR retrieves the control data for that RRID. The control data specifies not only the actual program to call, but also the following characteristics of the call:

- Which additional libraries to add to the library list prior to calling the program
- Which user profile to swap to for the call

RXSRTR control data is held in the RXSRTRCTL file. Each record has a key of the RRID and contains a 'routing data' field. Within the routing data field, various flag/value pairs can be specified to control what program will be called for the given RRID.

The format of the DATA field is as follows:

`{-flag value -flag value -flag value...}`

where flag can be one of a number of special values as follows:

| Flag | Description of associated value |
|------|---|
| `pgm` | Name of application program to be called **(required)** |
| `lib` | Name of application library |
| `usrprf` | 	User profile to run as |
| `passwd` | 	Password for user profile |
| `liblst` | 	Comma-separated library list (10 libraries max) or qualified job description name |
| `cvtopt` | 	Comma-separated conversion options |
| `response` | Response type for non-CGI programs |
| `actgrp` | 	Activation group |
| `prdlib` | 	Product library |


Note the following specifications related to control data flags and the way they must be specified in the DATA field in the RXSRTRCTL file:

- Each flag must be prefixed with a minus-sign, e.g. -pgm
- Values are automatically converted to upper-case for processing
- Flag/value pairs can be specified in any order
- Except for the pgm flag, all flag/value pairs are optional
- Comma-separated lists (for the liblst and cvtopt flags) must contain no embedded blanks

Note that there is a special *CONFIG record shipped in the RXS router control file which contains different valid flag values. See [RXS Router Configuration](#rxs-router-configuration) for more details.

## Control Flag Details

### pgm 
The **pgm** flag defines the program to be called. It can either be a valid i5/OS name or a special value. Valid special values are \*0-\*9. If a special value is used, the corresponding SCRIPT_NAME path segment is used as the program name, where 0 is the RRID itself, 1 is the next segment and so on. For instance, if RXSRTR is called with the following URL:

`http://192.168.0.1/test/john/mike/fred`

then TEST will be used as the RRID. If the TEST record in RXSRTRCTL has -pgm *1 specified, then JOHN will be used as the program name, if -pgm *2 is specified, MIKE will be used as the program name, and so on.

### lib 
The **lib** flag defines the library of the program to be called. If the library is not in the library list, it will be added to the top of the library list. If the lib flag is not specified, *LIBL will be used.

### usrprf/passwd 
The **usrprf** and **passwd** flags define the user profile which the job should use whilst calling the program. If **usrprf** is specified, **passwd** must also be specified and vice-versa. If either the specified user profile or password are invalid or if the user profile is disabled, RXS Router will return an error to the browser and will not call the program.

### liblst 
The **liblst** flag defines the library list which should be used for the request. The liblst flag can be specified in one of two ways:

- liblst library1,library2,library3
- liblst library/jobd

If a comma-separated list of libraries is specified, they are each added in order to the bottom of the library list. If a qualified job description (*JOBD) is specified, the initial library list (INLLIBL) of the *JOBD is retrieved and the libraries are each added in order to the bottom of the library list. All libraries added as a result of the liblst flag are removed when control is returned from the called program.

### cvtopt 
See the [Control Data Conversion Options](#control-data-conversion-options) section for details of how the **cvtopt** flag should be specified.

### response 
See the [Calling non-CGI programs](#calling-non-cgi-programs) section for details of how the **response** flag should be specified.

### actgrp 
The **actgrp** flag defines the name of the activation group in which the program specified with the **pgm** flag will run. This value is ignored unless the program is specified with ACTGRP(\*CALLER). If the special value **-actgrp \*dftactgrp** is specified, the value specified for the **dftactgrp** flag in the \*CONFIG record will be used. If the **actgrp** flag is not specified and the program is defined with ACTGRP(\*CALLER), it will run in the same activation group as RXS Router - by default, this is the RXSRTR activation group.

### prdlib 
The **prdlib** flag specifies the name of the product library which is added to the library list prior to the program specified in the **pgm** flag being called. The product library is added above all user libraries, including the current library. This library will be removed after the program is called. If a value is specified for the **dftprdlib** flag in the *CONFIG record, that library will be overwritten by this value while the program is being called, and will be replaced afterwards.

If the **prdlib** flag is not specified, but the **dftprdlib** flag is specified in the \*CONFIG record, that product library will be used for the duration of the program call.

If the **prdlib** flag is not specified and the **dftprdlib** flag is not specified in the \*CONFIG record, no product library will be used for the duration of the program call.

## Example RXSRTRCTL Record

| ID | DATA |
|---|---|
| ZAP | -pgm zappgm -liblst qgpl/zapjobd -lib qgpl -passwd donald -usrprf goofy |

## Control Data Conversion Options

Depending on the value of the cvtopt control flag, RXSRTR may perform certain steps prior to calling the specified program. Some of the conversion options are designed to allow existing CGI programs which expect a particular value for the SCRIPT_NAME or QUERY_STRING environment variables to continue to be called as before, even though you may have changed the calling URL to implement RXSRTR control. Multiple conversion options can be combined in a comma-separated list. Each conversion option will be processed in the order it is found. The following sections describe the conversion options.

### rm_sn (Remove RRID from SCRIPT_NAME) 
The rm_sn conversion option indicates that if the RRID is included in the URL, it will be removed from the SCRIPT_NAME environment variable prior to the CGI program being called.

If the rm_sn conversion option is specified and the URL used to call RXSRTR is:

`http://192.168.0.1/rxsrtr/db2wse/mylib/myfile`

then if the CGI program retrieves the SCRIPT_NAME environment variable, it will retrieve a value of

`/db2wse/mylib/myfile`

rather than the expected value of

`/rxsrtr/db2wse/mylib/myfile`

### rm_qs (Remove RRID from QUERY_STRING) 
The rm_qs conversion option indicates that if the RRID is included in the URL, it will be removed from the QUERY_STRING environment variable prior to the CGI program being called.

If the rm_qs conversion option is specified and the URL used to call RXSRTR is

`http://192.168.0.1/cust/?fname=john&rxsrtr=mickey&lname=smith`

then if the CGI program retrieves the QUERY_STRING environment variable, it will retrieve a value of

`fname=john&lname=smith`

rather than the expected value of

`fname=john&rxsrtr=mickey&lname=smith`

### sn_pr (Parse parameters from SCRIPT_NAME) 
The sn_pr conversion option indicates that the program being called will be passed a number of parameters, as specified in the SCRIPT_NAME environment variable, where every path segment after the RRID will be treated as a separate character parameter.

If the URL used to call RXSRTR is:

`http://192.168.0.1/zap/mickey/mouse`

and the ZAP control record has the following value:

`-pgm zappgm -lib qgpl -cvtopt sn_pr`

then RXSRTR will call program QGPL/ZAPPGM as follows:

`CALL PGM(QGPL/ZAPPGM) PARM('MICKEY' 'MOUSE')`

All parameters passed to the specified program will be passed as string pointers. In RPG, this means that they should be defined as follows:

```rpgle
     D ZAPPGM          PR                  Extpgm('ZAPPGM')
     D   fname                         *   Const Options(*String)
     D   lname                         *   Const Options(*String)
```

## Calling Non-CGI Programs

CGI programs work by writing their output (typically the HTML for a web page) to 'standard output'. When the CGI program ends and control is returned to the HTTP server, the standard output is sent to the browser, displaying as a web page. However, if you use the HTTP server to call a non-CGI 'application' program, because it doesn't write to standard output, an HTTP 500 error page will appear.

However, RXSRTR allows you to call a non-CGI application program, with the following limitations:

- The program must be a batch (non-interactive) program
- A maximum of 10 parameters can be passed to the program
- All parameters must be defined in the program as character strings with a maximum length of 256 bytes

If you wish to pass parameters to the program, you must specify the **sn_pr** conversion option, which will pass each of the SCRIPT_NAME path segments following the RRID as a character parameter. See the [Control Data Conversion Options](#control-data-conversion-options) section for details on how to specify this conversion option and the calling URL. When calling a non-CGI program, you must configure RXSRTR to send a response to the browser as an ad-hoc web-page, by specifying the response flag. It can be specified in one of the following two ways:

`-response *confirm`

or

`-response *msg`

After RXSRTR has called the program, it checks the value of the response flag. If -response *confirm is specified in the control record, RXSRTR will send a generic "Program called successfully" message to the browser. If -response *msg is specified, RXSRTR will retrieve the last message sent to it from the application program and will write it out to the browser.

For instance, if an application program is typically called from a command-line or from a green-screen menu and sends a completion message (e.g. "Audit record created") to the screen, calling this program from RXSRTR and specifying -response *msg in the RXSRTRCTL record will cause this same message to be sent to the browser.

## Initializing RXS Router

For performance reasons, when the RXS Router is first called within a job, it reads the records from the RXS Router control file RXSRTRCTL and caches the data into a user space called RXSRTRCTLU in the same library where the RXS Router itself exists. On subsequent calls within the same job, it uses the cached values, thus eliminating costly file I/O. However, if records in the RXSRTRCTL file are added, deleted or updated, the RXS Router cache must be updated.

A command called [INZRXSRTR (Initialize RXS Router)](https://isupport.katointegrations.com/rxs/3.3/inzrxsrtr.md) is supplied with the RXS Router. When this command is run from a command line, the RXS Router cache is updated to contain the current values from the RXSRTRCTL file.

This command can also be used to refresh the cached call pointers in Apache CGI jobs. This is useful because normally OS/400 will cache programs for reuse, but when you are doing web service development you will want Apache to pick up the latest version of the program after compiling it. Normally one would have to either recompile the entry level program to clear all cached call pointers or restart Apache. Neither of those options are optimal, so that is why we introduced the INZRXSRTR command to effectively refresh all cached call pointers.

## RXS Router Configuration

Additional RXS Router configuration can be controlled by changing the options in the shipped *CONFIG record in the RXS Router control file RXSRTRCTL. The various control options are as follows:

`routingid`
: Routing identifier

`loglvl`
: Logging level (0, 1, or 2)

`allowreset`
: Allow *RESET option from client (*YES or *NO)

`debugmode`
: Run in debug mode(*YES or *NO)

`dftactgrp`
: Specifies the default activation group for CGI programs

`dftliblst`
: Specifies the default library list for CGI programs

`dfterrpgm`
: Specifies the default error-handling program

`dftprdlib`
: Specifies the default product library to use

The *CONFIG record in the RXSRTRCTL file is shipped as follows:

`-routingid rxsrtr -loglvl 0 -allowreset *no -debugmode *no`

### Control Option: routingid
The **routingid** flag defines the type of routing that should be performed by RXSRTR. Depending on its value, RXSRTR will use either 'static' or 'dynamic' routing to determine the value of the request routing identifier (RRID), which actually defines the call definition. If the **routingid** flag is not specified, a value of 'RXSRTR' is assumed.

For more information about how RXS Router retrieves and uses the routingid, see the page [Configuring RXS Router](https://isupport.katointegrations.com/rxs/3.3/configuring_rxs_router.md).

### Control Option: loglvl
The **loglvl** flag defines the level of logging that will be performed by RXSRTR.

There are three different logging levels available within RXSRTR:

- **Log Level 0 (Error)** - Only errors encountered by RXSRTR during initialization or when processing a request are logged.
- **Log Level 1 (Basic)** - All initialization information is logged, as well as basic information about each request, including user profile swaps, addition of libraries to the library list, routing identifiers used and the SCRIPT_NAME and QUERY_STRING values used for the request
- **Log Level 2 (Full)** - Complete information about each request is logged, including parameter values and response data (where appropriate)

All log entries are written to the RXSRTRLOG file, which contains a 1024-byte free-format field called DATA. Each log record consists of the following:

| Start positionin DATA field | Length of data | Data |
|:--------------------------:|---|:--------------:|
| 1 | 1 | Log level ('0', '1', or '2') of log entry |
| 3 | 26 | Qualified HTTP server job name |
| 30 | 26 | Log entry timestamp |
| 57 | 967 | Log entry data |


If logging level 1 is specified, all logging at level 0 is also included. If logging level 2 is specified, all logging at logging level 0 and logging level 1 is also included.

If the **loglvl** flag is not specified or is set to a value other than 0, 1 or 2, logging level 0 is assumed.

### Control Option: allowreset
If the **allowreset** flag is set to \*YES, a user can reset the RXS Router from the browser by specifying a value of '\*RESET' in the URL (e.g. 'http://192.168.0.1/rxsrtr/*reset'). This will cause the RXS Router to reload all control records from the RXSRTRCTL file. It is recommended that a value of *YES is specified only if the RXS Router is being used on a development machine, since there is no checking to ensure which client used the reset option.

If the **allowreset** flag is not specified or is set to a value other than *YES, a value of *NO is assumed.

### Control Option: debugmode
If the **debugmode** flag is set to *YES, RXSRTR will run in debug mode. In this mode, instead of calling the specified program, RXSRTR will return a page displaying details of the program which would be called, as well as any the values of the QUERY_STRING and SCRIPT_NAME environment variables (if they have been changed due to any conversion options specified with the cvtopt flag).

If the **debugmode** flag is not specified or is set to a value other than *YES, a value of *NO is assumed.

### Control Option: dftactgrp
If the **dftactgrp** flag is specified, all programs called by RXS Router which are compiled with ACTGRP(*CALLER) will run using the specified activation group (unless the actgrp flag is specified in the RRID record for the program being called, in which case that value will be used instead).

If the **dftactgrp** flag is not specified, all programs called by RXS Router which are compiled with ACTGRP(*CALLER) will run in the same activation group as RXS Router - by default this activation group is called RXSRTR.

All programs called by RXS Router which are compiled with ACTGRP(*NEW) or ACTGRP(named-activation-group)) will run in the specified activation group.

### Control Option: dftliblst
If the **dftliblst** flag is specified, the defined library list or list of libraries will be used for all CGI program calls. This flag can be provided either a comma-separated list of libraries or a qualified job description. If a comma-separated list of libraries is specified, there must be no spaces between each library and each comma. If a qualified job description is specified, e.g. -dftliblst qgpl/jobd, the initial library list (INLLIBL) for that job description is used. In either case, the specified list of libraries will replace the user portion of the CGI job's library list. No validation is performed when this flag is specified to check whether the job description or libraries exist, but runtime errors will occur.

If the **dftliblst** flag is not specified, CGI program calls will use the default library list defined for the HTTP server (although see also the dftprdlib flag, which can alter the library list for CGI program calls).

### Control Option: dfterrpgm
If the **dfterrpgm** flag is specified, the specified error-handling program will be called if the RXS Router encounters an error either when calling a specified program or during other processing.

See the RXS Router user manual for details of the interface to the error handler and an example error handler.

If the **dfterrpgm** flag is not specified, no default error handling program will be used.

### Control Option: dftprdlib
If the **dftprdlib** flag is specified, the specified product library will be added to the library list when the RXS Router is called and is removed when the RXS Router returns control to the HTTP server. The product library is added above all user libraries, including the current library.

If a value is specified for the prdlib flag in a user (non-*CONFIG) control record, this library will be overwritten by that value while the program is being called, and will be replaced afterwards.

If the **dftprdlib** flag is not specified, no default product library will be used.

---

# Create RXSRTR Environment (CRTRXSRTRE)

> Creates a new RXS Router environment for use with RXSRTR.

The Create RXSRTR Environment (CRTRXSRTRE) command allows the user to create a new environment for use with the [RXS Router](https://isupport.katointegrations.com/rxs/3.3/rxsrtr.md).

## Parameters

### Environment (ENV)
This is a required parameter.

Specify the name of the RXS Router environment to create.

#### Possible Values:
`character-value`
: Name of the RXS Router environment to create.

### Text 'description' (TEXT)
Describes the environment being created.

#### Possible Values:
`*BLANK`
: No description.

`character-value`
: Description of the RXS Router environment being created.

### Copy from environment (FROMENV)
Specify the name of the existing environment from which to copy the control records.

#### Possible Values:
`*DFT`
: The new environment is copied from the default RXSRTR environment.

`character-value`
: Specify the name of the environment to copy.

### Records to copy (CPYENVOPT)
Specify which records to copy from the existing environment.

#### Possible Values:
`*ALL`
: All control records are copied from the specified environment.

`*CFG`
: Only the *CONFIG record is copied from the specified environment.

### Data library (DTALIB)
Specify the RXS Router data library. The RXS Router data library contains data objects required by the RXS Router runtime, including copies of the control file, log file and data cache.

#### Possible Values:
`*LIBL`
: The first library in the library list of the current job which contains the objects specified above will be used.

`data-library`
: The specified library will be used.

---

# Work with RXSRTR Entries (WRKRXSRTRE)

> Interactive command to create, modify, and delete RXS Router routing entries in RXSRTRCTL.

The Work with RXSRTRCTL Entries (WRKRXSRTRE) command allows the user to create, modify, and delete configuration entries for use with the RXS Router.

The command consists of two screens - the main entry display screen, and the entry detail display screen.

From the main entry display screen, you can select one or more entries to be displayed, edited or deleted, using the subfile selector field on the left side. You can also use the F6 key to create a new entry. You can use the input field at the top of the screen to position to an entry.

![Work with RXSRTRCTL Entries](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/maindisplay.png)

On the main entry display screen, only the first few characters of each entry are displayed.

On the entry detail display screen, you can display the full entry details and edit them if necessary. Each entry consists of a string comprised of one or more 'flags' followed by a related value.

![RXSRTRCTL Entry Detail](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/entrydetail.png)

For more information about how to create a valid RXS Router entry to meet your specific needs, please read more about [RXS Router](https://isupport.katointegrations.com/rxs/3.3/rxsrtr.md).

## Parameters

### Environment (ENV)
Specifies the name of the RXS Router environment to edit. This corresponds to same-named members in the RXSRTRCTL and RXSRTRLOG files and a user space of the same name.

#### Possible Values:
`*DFT`
: The default environment called RXSRTR will be used.

`character-value`
: Name of the RXS Router environment to work with.

### Data library (DTALIB)
Specify the RXS Router data library. The RXS Router data library contains data objects required by the RXS Router runtime, including copies of the control file, log file and data cache.

#### Possible Values:
`*LIBL`
: The first library in the library list of the current job which contains the objects specified above will be used.

`data-library`
: The specified library will be used.

### Routing Identifier (ID)
Specify the routing control entry to work with.

#### Possible Values:
`*ALL`
: All routing control entries will be displayed. The user can specify entries for display, edit or delete or can create a new entry.

`routing-identifier`
: The specified routing control entry will be displayed in edit mode.

---

# Initialize RXS Router (INZRXSRTR)

> Refreshes the RXS Router cache from RXSRTRCTL and clears cached call pointers in Apache CGI jobs.

The INZRXSRTR (Initialize RXS Router) command is supplied with the RXS Router. When this command is run from a command line, the RXS Router cache is updated to contain the current values from the RXSRTRCTL file.

This command can also be used to refresh the cached call pointers in Apache CGI jobs. This is useful because normally OS/400 will cache programs for reuse, but when you are doing web service development you will want Apache to pick up the latest version of the program after compiling it. Normally one would have to either recompile the entry level program to clear all cached call pointers or restart Apache. Neither of those options are optimal, so that is why we introduced the INZRXSRTR command to effectively refresh all cached call pointers.

## Parameters

### Environment (ENV)
Specifies the name of the RXS Router environment to initialize. This corresponds to same-named members in the RXSRTRCTL and RXSRTRLOG files and a user space of the same name.

Note: If the RXSRTR environment does not exist, no error message will be displayed.

#### Possible Values:
`*ALL`
: All RXSRTR environments will be initialized.

`*DFT`
: The default environment called RXSRTR will be initialized.

`character-value`
: Name of the RXSRTR environment to initialize.

### Data library (DTALIB)
Specify the RXS Router data library. The RXS Router data library contains data objects required by the RXS Router runtime, including copies of the control file, log file and data cache.

#### Possible Values:
`*LIBL`
: The first library in the library list of the current job which contains the objects specified above will be used.

`data-library`
: The specified library will be used.

---

# Build Template (BLDTPL)

> Generates a compiled template (.tpl) file from a sample XML document for use with the RXS composition engine.

The BLDTPL command aids in composing RPG-XML Suite template (*.tpl) files. These are then converted using the CRTRPGTPL Command, which are in turn used with the Template/Composition Engine subprocedures (RXS_StartComposeEngine(), RXS_ComposeSection, RXS_ComposeVariable(), and RXS_GetComposeBuffer()) to compose XML documents.

**Note: You will need to make manual modifications to the generated .tpl file prior to using it with the CRTRPGTPL Command.** 

**Please follow our example on [Creating an RXS Template](https://isupport.katointegrations.com/rxs/3.3/creating_an_rxs_template.md) to understand this process.**

## Parameters

### IFS XML Location (IFSXMLLOC)
This is a required parameter.

Specify an IFS file path to the stream file containing your XML to build the template from. **This must be a fully qualified file path.**

#### Possible Values:
`character-value`
: IFS path to XML file to build template from.

### IFS Template Location (IFSTPLLOC)
This is a required parameter.

Specify an IFS file path to the location the generated .tpl file should be build. **This must be a fully qualified file path.**

#### Possible Values:
`character-value`
: IFS path to create .tpl file at.

### Indent Length (INDENT)
Specify how many spaces should be used when indenting the created .tpl file.

#### Possible Values:
`numeric-value`
: Number of spaces used for indentation.

---

# Create RPG Template (CRTRPGTPL)

> Compiles an RXS template source file (.tpl) into a bound service program for use in RPG composition engine calls.

Use this command to create a RPG version of any template files needed for your program. Starting with version 3.0 of RPG-XML Suite, you no longer reference template files in the IFS at runtime. Instead, use this command to convert the stream file to an RPG version of the template. The RPG source is created for you from the template, and if the template should ever change, you will need to re-run this command and recompile your RPG program.

If you don't currently have an RXS template file, you will need to use [BLDTPL](https://isupport.katointegrations.com/rxs/3.3/bldtpl.md) to build one.

## Parameters

### Stream file (STMF)
This is a required parameter.

Specifies the full path of the IFS stream file built manually or with the BLDTPL Command.

#### Possible Values:
`character-value`
: Qualified filepath of IFS stream file.

### Source file (FILE)
Specifies the name of the source and library of the physical file that will contain the generated RPG source code.

#### Possible Values:
`character-value`
: Name of the source physical file in which the template member will be generated.

### Member (MBR)
The source member that with contain the generated RPG source code. Note that the default value of this parameter - *FIRST - is a placeholder and must be overwritten.

#### Possible Values:
`character-value`
: Name of the source member.

### Sections DS Name (SECTIONSDS)
The name given to the Sections data structure in the generated code. Setting this value allows the section and variable data structures to be qualified, and is recommended.

#### Possible Values:
`*NONE`
: No name is given to the Sections data structure.

`character-value`
: This name will be assigned to the Sections data structure.

### Variables DS Name (VARSDS)
The name given to the Variables data structure in the generated code. Setting this value allows the section and variable data structures to be qualified, and is recommended.

#### Possible Values:
`*NONE`
: No name is given to the Variables data structure.

`character-value`
: This name will be assigned to the Variables data structure.

### Coded Character Set Id (CCSID)
The CCSID of the IFS stream file to be processed.

#### Possible Values:
`*JOB`
: The CCSID of the current job is used.

`character-value`
: The specified CCSID is used.

### Trim Template Lines (TRIMLINES)
Control whether leading and trailing whitespace will be trimmed from template lines. Preserves carriage returns/line feeds.

#### Possible Values:
`*NO`
: Whitespace will be preserved.

`*YES`
: Leading and trailing whitespace will be trimmed.

---

# Build Parsing Subprocedure (BLDPRS)

> Generates an RPG handler subprocedure for XML or JSON event-based parsing from a sample document.

The BLDPRS command is used to aid in writing RPG-XML Suite XML parsing code to be used with the [RXS_Parse() subprocedure](https://isupport.katointegrations.com/rxs/3.3/rxs_parse.md). RXS_Parse() relies on a "handler" subprocedure which can capture various XML parsing events, giving you access to the data in an XML document.

Note: You will need to make manual modifications to the generated parsing "handler" in addition to importing it into your program.

Please follow our example on [Creating an XML Parsing Subprocedure](https://isupport.katointegrations.com/rxs/3.3/creating_an_xml_parsing_subprocedure.md) to understand this process.

## Parameters

### Source Library (SRCLIB)
This is a required parameter.

Specify the library name where your parsing handler subprocedure source file member should be created.

#### Possible Values:
`character-value`
: Library name to create the source file member in.

### Source File (SRCPF)
This is a required parameter.

Specify the file name where your parsing handler subprocedure source file member should be created.

#### Possible Values:
`character-value`
: File name to create the source file member in.

### Source Member (SRCMBR)
This is a required parameter.

Specify the member name where your parsing handler subprocedure source file member should be created.

#### Possible Values:
`character-value`
: Member name to hold the generated parsing handler subprocedure.

### IFS XML Location (IFSXMLLOC)
This is a required parameter.

Specify an IFS file path to the stream file containing your XML to build the parsing handler subprocedure from. This must be a fully qualified file path.

#### Possible Values:
`character-value`
: IFS path to XML file to build parsing handler subprocedure from.

### Base Envelope (BASEENV)
This is a required parameter.

Specify the beginning portion of an XPath that is repeated for every element or attribute in the XML document.

#### Possible Values:
`character-value`
: XPath to use as the base path to simplify code generation.

### Generate Content Parsing (XMLCONTENT)
Specify whether or not XML "content" events should be captured in the generated parsing handler subprocedure.

#### Possible Values:
`*YES`
: Generate XML "content" events

`*NO`
: Do not generate XML "content" events

### Generate Begin Element Parsing (XMLBEGIN)
Specify whether or not XML "begin element" events should be captured in the generated parsing handler subprocedure.

#### Possible Values:
`*YES`
: Generate XML "begin element" events

`*NO`
: Do not generate XML "begin element" events

### Generate End Element Parsing (XMLBEGIN)
Specify whether or not XML "end element" events should be captured in the generated parsing handler subprocedure.

#### Possible Values:
`*YES`
: Generate XML "end element" events

`*NO`
: Do not generate XML "end element" events

### Generate Attribute Parsing (XMLBEGIN)
Specify whether or not XML "attribute" events should be captured in the generated parsing handler subprocedure.

#### Possible Values:
`*YES`
: Generate XML "attribute" events

`*NO`
: Do not generate XML "attribute" events

---

# Creating an RXS Template

> Tutorial for creating a compiled XML template using BLDTPL and CRTRPGTPL, covering variable placeholders and section declarations.

To generate XML, RPG-XML Suite relies on what is referred to as the "template engine" or "composition engine". This engine uses a specially marked up psuedo-XML file divided into sections and with variable fields embedded in it as well as a set of RPG-XML Suite subprocedures. The combination of the template file and the subprocedures allow you to build XML of any complexity or depth needed to meet your business requirements.

This tutorial will demonstrate using the following commands in sequence to generate a compiled XML template:

- [BLDTPL](https://isupport.katointegrations.com/rxs/3.3/bldtpl.md)
- [CRTRPGTPL](https://isupport.katointegrations.com/rxs/3.3/crtrpgtpl.md)

Once our compiled template is generated, we will also write a short example program.

## Generating the IFS Template File (BLDTPL)

To start with creating a template file, we first need to create a file in the IFS. This file will be populated with our sample XML data, which will be used to generate the template. The command below uses QSHELL to create an IFS stream file using CCSID 819. This CCSID is **strongly** recommended when creating IFS files for use with RXS tools. Note that this command is **case-sensitive**.

`QSH CMD('touch -C 819 /www/myrxs/templates/postadr.xml')`

Note that, for the purposes of this tutorial, we're generating this example file in the /www/myrxs/templates directory. For many of our customers, this directory was created during their initial installation of RPG-XML Suite. This is not the only valid location for template files in the IFS - you can generate and store your XML and template files in any directory.

Next, we need to populate this file with a sample of the XML that we're going to use as the basis of the template. This sample document should be as complete as possible. If your XML has many optional fields, try to include them all in this sample file, even if some of them would not logically appear with others in normal usage. To get the XML into our IFS stream file, we'll use the EDTF command:

`EDTF '/www/myrxs/templates/postadr.xml'`

Now, paste your sample XML into the resulting editor window. This sample XML may or may not contain data in any or all fields - this will not impact the template output.

```xml
<PostAdr residential="true">
  <name title="Mx.">
    <first>Jamie</first>
    <last>Hale</last>
  </name>
  <street>2886 Veltri Dr</street>
  <city>Hickory Hills</city>
  <state>VA</state>
  <zip>94124</zip>
  <phone>949-555-4671</phone>
  <phone></phone>
</PostAdr>
```

While in the Edit File editor select F2 to save the document changes. Once the sample XML file is saved, call the [BLDTPL](https://isupport.katointegrations.com/rxs/3.3/bldtpl.md) command and specify the fully-qualified filepath for the sample XML file in the first parameter, and the fully-qualified filepath for where the generated .tpl template file should be written in the second parameter:

`BLDTPL SRCSTMF('/www/myrxs/templates/postadr.xml') OUTSTMF('/www/myrxs/templates/postadr.tpl') INDENT(2)`

The third parameter, `INDENT`, is optional and controls how many spaces are used to indent each nested element in the generated template file. The default value is 2.

There are two primary components to an RPG-XML Suite XML template file:

1. **Variables** are placeholders used to indicate where data will be composed into the XML document by the [RXS_ComposeVariable()](https://isupport.katointegrations.com/rxs/3.3/rxs_composevariable.md) subprocedure. These are declared with the format `.:variable:.`.
2. **Sections** are used to denote a "chunk" of the XML document that should be written to the buffer in a single call to [RXS_ComposeSection()](https://isupport.katointegrations.com/rxs/3.3/rxs_composesection.md). Sections are declared with the format `::section`. Each template must include a **minimum of one section**, and the first line in any template must be a section declaration. Sections are not automatically generated in the BLDTPL output - you will need to manually add them yourself based on your knowledge and understanding of how the document is meant to be composed and would best be divided.

Below is the generated template file located at /www/myrxs/templates/postadr.tpl - use EDTF to view it. Note that all elements and attributes have had their values replaced with the variable placeholders respective to the element or attribute's name. Also note that there are no sections present in the generated output.

```xml
<PostAdr residential=".:residential:.">
  <name title=".:title:.">
    <first>.:first:.</first>
    <last>.:last:.</last>
  </name>
  <street>.:street:.</street>
  <city>.:city:.</city>
  <state>.:state:.</state>
  <zip>.:zip:.</zip>
  <phone>.:phone:.</phone>
</PostAdr>
```

An important thing to note is that in the generated template file, there is only one `<phone>` element, where before there were two. Repeating elements are generally handled using sections, as described below, rather than duplicating lines in the template.

Now that our template is generated with the variable placeholders, we need to add our sections. We'll need to add at least one section at the beginning of the document. Since our `<phone>` element is a repeating element, we will give that its own section as well. Finally, we'll add a closing section to write the end tags for the document.

Here is an example of our edited template file, including the sections we've added:

```xml
::PostAdr_beg
<PostAdr residential=".:residential:.">
  <name title=".:title:.">
    <first>.:first:.</first>
    <last>.:last:.</last>
  </name>
  <street>.:street:.</street>
  <city>.:city:.</city>
  <state>.:state:.</state>
  <zip>.:zip:.</zip>
::Phone
  <phone>.:phone:.</phone>
::PostAdr_end
</PostAdr>
```

By giving the `<phone>` element a separate section, it gives us the flexibility to write as many of those elements as we need by calling RXS_ComposeVariable() to populate the `phone` variable, then RXS_ComposeSection() to write the `Phone` section for that phone number.

Not all sections in an XML document need to be used during XML composition. Sections of your XML may be optional - unused depending upon your business logic or a specific request's needs. In our example, if we did not have a phone number for a given address, we may not compose the `Phone` section for that entry at all.

## Generating the Compiled RPG Template (CRTRPGTPL)

Now that we have our IFS template file set up with our sections, we are going to use the [CRTRPGTPL](https://isupport.katointegrations.com/rxs/3.3/crtrpgtpl.md) command to convert our template file into an RPG source member. This RPG source member will be compiled directly into your RPG-XML Suite-enabled program using the `/COPY` compiler directive.

For demonstration purposes, we will generate the compiled template into the QRPGLETPL source physical file in the RXS library. It is **strongly recommended** that you never store your code members in any RXS library.

**Note:** the command will specify a name of *FIRST for the member by default. You need to change this value - we generally recommend specifying the same name as the program you're going to use this template with, or the name of the IFS template file.

`CRTRPGTPL STMF('/www/myrxs/templates/postadr.tpl') FILE(RXS/QRPGLETPL) MBR(POSTADR) SECTIONSDS(S) VARSDS(V)`

It's also important to note that we're specifying values for SECTIONSDS and VARSDS. These parameters are not set by default, but specifying values allows CRTRPGTPL to build qualified data structures for the section and variable names. This helps improve code readability and helps keep the template engine fields separate from your normal RPG code. This is not required, but very strongly recommended.

Once this command has completed, you can open up the source physical file RXS/QRPGLETPL and look at member POSTADR using PDM or RDi. It should look like this:

```rpgle
     /IF NOT DEFINED(POSTADR)
      /DEFINE POSTADR
     D S...
     D                 DS                  Qualified
     D  PostAdr_beg...
     D                               50A   Varying
     D  phone...
     D                               50A   Varying
     D  PostAdr_end...
     D                               50A   Varying
     D V...
     D                 DS                  Qualified
     D  residential...
     D                               30A   Varying
     D  title...
     D                               30A   Varying
     D  first...
     D                               30A   Varying
     D  last...
     D                               30A   Varying
     D  street...
     D                               30A   Varying
     D  city...
     D                               30A   Varying
     D  state...
     D                               30A   Varying
     D  zip...
     D                               30A   Varying
     D  phone...
     D                               30A   Varying
      /EOF
      /ENDIF
      /free

         S.PostAdr_beg =
           'PostAdr_beg';
         S.phone =
           'phone';
         S.PostAdr_end =
           'PostAdr_end';
         V.residential =
           'residential';
         V.title =
           'title';
         V.first =
           'first';
         V.last =
           'last';
         V.street =
           'street';
         V.city =
           'city';
         V.state =
           'state';
         V.zip =
           'zip';
         V.phone =
           'phone';

        p  = '';
        p += '::PostAdr_beg' + x'15';
        p += '<PostAdr residential="' + '.:residential:.' + '">' + x'15';
        p += '  <name title="' + '.:title:.' + '">' + x'15';
        p += '    <first>' + '.:first:.' + '</first>' + x'15';
        p += '    <last>' + '.:last:.' + '</last>' + x'15';
        p += '  </name>' + x'15';
        p += '  <street>' + '.:street:.' + '</street>' + x'15';
        p += '  <city>' + '.:city:.' + '</city>' + x'15';
        p += '  <state>' + '.:state:.' + '</state>' + x'15';
        p += '  <zip>' + '.:zip:.' + '</zip>' + x'15';
        p += '::phone' + x'15';
        p += '  <phone>' + '.:phone:.' + '</phone>' + x'15';
        p += '::PostAdr_end' + x'15';
        p += '</PostAdr>' + x'15';

      /end-free
```

As you can see, the resulting template file is quite literally RPG code. The section and variable names have been added to the S and V data structures, and the template content itself is included below in the format expected by the XML composition engine. One of the benefits of this approach is that, because the template section and variable names are RPG fields, it's very easy to catch typos in your program because the program simply won't compile.

**Note: It is imperative that you *never edit or modify* the source member generated by the CRTRPGTPL command in any way, unless directed to do so by a member of our support staff.** If you need to make changes to your template, first update the corresponding .tpl file in the IFS, then run CRTRPGTPL on that updated .tpl file to regenerate the compiled template member.

## Using the RPG Template With the Composition Engine

Now that we have our compiled template, we will bring this template into our RPG program and use the XML composition engine subprocedures to build our XML. Here is an example program using our POSTADR template:

```rpgle
     H DEBUG(*YES) DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,POSTADR

     D Template        PR
     D  p                                  Like(RXS_TEMPLATE_PARM)

     D XML             S                   Like(RXS_Var64Kv_t)
     D i               S              3I 0

     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
     D                                     Inz(*LikeDS)

      /free
        monitor;

          RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
          ComposeDS.TemplateProcedure = %Paddr(Template);
          RXS_StartComposeEngine( ComposeDS );

          RXS_ComposeVariable( V.residential : 'true');
          RXS_ComposeVariable( V.title : 'Mr.' );
          RXS_ComposeVariable( V.first : 'Sample' );
          RXS_ComposeVariable( V.last : 'Resident' );
          RXS_ComposeVariable( V.street : '999 Totally A Real Street' );
          RXS_ComposeVariable( V.city : 'Cityville' );
          RXS_ComposeVariable( V.state : 'OH' );
          RXS_ComposeVariable( V.zip : '12345' );
          RXS_ComposeSection( S.PostAdr_beg );

          for i = 1 to 3 by 1;
            RXS_ComposeVariable( V.phone : '987-654-321' + %Char(i) );
            RXS_ComposeSection( S.phone );
          endfor;

          RXS_ComposeSection( S.PostAdr_end );

          XML = RXS_GetComposeBuffer();

          RXS_JobLog( XML );
        on-error;

        endmon;
        *Inlr = *On;

      /end-free


     P Template        B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)
      /copy QRPGLETPL,POSTADR
     P                 E
```

The end result of this program will be XML written out to the job log that looks like the following:

```xml
<PostAdr residential="true">
  <name title="Mx.">
    <first>Jamie</first>
    <last>Hale</last>
  </name>
  <street>2886 Veltri Dr</street>
  <city>Hickory Hills</city>
  <state>VA</state>
  <zip>94124</zip>
  <phone>949-555-4671</phone>
  <phone>949-555-4672</phone>
  <phone>949-555-4673</phone>
</PostAdr>
```

---

# Creating an XML Parsing Subprocedure

> Tutorial for generating an XML event handler subprocedure using BLDPRS, covering event type selection and integration into RPG programs.

The BLDPRS command is used to aid in writing RPG-XML Suite XML parsing code to be used with the [RXS_Parse()](https://isupport.katointegrations.com/rxs/3.3/rxs_parse.md) subprocedure. In the examples included with RPG-XML Suite (i.e. MYRXS/EXAMPLE,*) there are many "handler" or parsing subprocedures that take an XML file and parse it for the data contents. After writing a few of these parsing subprocedures you will find that much of keying is repetitive with just a few things changing from program to program. Instead of doing this manually, the BLDPRS command can be used to generate a parsing subprocedure that can get you most of the way to a functioning parsing subprocedure.

BLDPRS usage can best be explained with an example. The first thing needed is an XML document residing in the IFS. Type the following on the command line to create an XML file named /home/bldprs001.xml in the IFS.

`QSH CMD('touch -C 819 /home/bldprs001.xml')`

The file now exists but without content. To add content we will use the EDTF command and copy/paste the following XML.

`EDTF '/home/bldprs001.xml'`

Add the following XML to the file:

```xml
<PostAdr residential="true">
  <name title="Mr.">
    <first>Sample</first>
    <last>Resident</last>
  </name>
  <street>123 Center Rd</street>
  <cty>Mankato</cty>
  <state>MN</state>
  <zip>56001</zip>
  <phone>123-123-1234</phone>
  <phone>321-321-4321</phone>
</PostAdr>
```

While in the Edit File editor select F2 to save the document changes. Now it is time to invoke the BLDPRS command to generate the parsing code. You will need to provide the library, source physical file, and source member of where you would like the generated code to be placed and also where the XML document resides in the IFS that should be used. The last parameter, BASEENV, can be omitted but serves a code readability purpose. BASEENV allows you to specify the beginning portion of an XPath that is repeated for every element, or attribute, in the XML document. In the below example /PostAdr is specified for the BASEENV parameter. This value will be applied to an RPG variable named baseEnv and baseEnv will then be used in the WHEN clauses to make the statements shorter.

`BLDPRS SRCLIB(RXS) SRCPF(EXAMPLE) SRCMBR(BLDPRS001) IFSXMLLOC('/home/bldprs001.xml') BASEENV('/PostAdr')`

The following is the contents of source member RXS/EXAMPLE,BLDPRS001 after running the above command. Note that some of the code was omitted for brevity's sake.

```rpgle
     D allHandler      pr
     D  pType                              value like(RXS_Type)
     D  pXPath                             value like(RXS_XPath)
     D  pData                              value like(RXS_XmlData)
     D  pDataLen                           value like(RXS_Length)


      //-----------------------------------------------------------------
      // @Author: 
      // @Created: 
      // @Desc:
      //-----------------------------------------------------------------
     P allHandler      b
     D allHandler      pi
     D  pType                              value like(RXS_Type)
     D  pXPath                             value like(RXS_XPath)
     D  pData                              value like(RXS_XmlData)
     D  pDataLen                           value like(RXS_Length)

     D chgMe           s                   like(RXS_XmlData)
     D baseEnv         s             70a   varying
      /free
        baseEnv = '/PostAdr';

        select;

        when pXPath = baseEnv +'>';
          chgMe = pData;
        when pXPath = baseEnv +'@residential';
          chgMe = pData;
        when pXPath = baseEnv +'/';
          chgMe = pData;
        when pXPath = baseEnv +'/name>';
          chgMe = pData;
        when pXPath = baseEnv +'/name@title';
          chgMe = pData;
        when pXPath = baseEnv +'/name/';
          chgMe = pData;
        when pXPath = baseEnv +'/name/first>';
          chgMe = pData;
        when pXPath = baseEnv +'/name/first/';
          chgMe = pData;
        when pXPath = baseEnv +'/name/first/>';
          chgMe = pData;
        when pXPath = baseEnv +'/name/last>';
          chgMe = pData;
        when pXPath = baseEnv +'/name/last/';
          chgMe = pData;
        when pXPath = baseEnv +'/name/last/>';
          chgMe = pData;
        when pXPath = baseEnv +'/name/>';
          chgMe = pData;
        when pXPath = baseEnv +'/street>';
          chgMe = pData;
        when pXPath = baseEnv +'/street/';
          chgMe = pData;
        when pXPath = baseEnv +'/street/>';
          chgMe = pData;
           ...
        when pXPath = baseEnv +'/>';
          chgMe = pData;
        endsl;
      /end-free
     P                 e
```

One thing you'll note is that by default BLDPRS will generate a parsing subprocedure meant to handle all possible events. However, Element Begin (e.g. /ELEMENT> ) and Element End (e.g. /ELEMENT/> ) events are frequently unused. BLDPRS has the capability to exclude certain types of events when generating the parsing code through the parameters XMLCONTENT, XMLBEGIN, XMLEND, and XMLATTR.

Each of those can have a value of *YES or *NO, but at least one of them must have a value of *YES or there will be no events to generate a subprocedure for. To generate a subprocedure with only Content (e.g. /ELEMENT/ ) events, you could do the following:

`BLDPRS SRCLIB(MYRXS) SRCPF(EXAMPLE) SRCMBR(BLDPRS001) IFSXMLLOC('/home/bldprs001.xml') BASEENV('/PostAdr') XMLCONTENT(*YES) XMLBEGIN(*NO) XMLEND(*NO) XMLATTR(*NO)`

This would generate an XML parsing subprocedure like this:

```rpgle
     D allHandler      pr
     D  pType                              value like(RXS_Type)
     D  pXPath                             value like(RXS_XPath)
     D  pData                              value like(RXS_XmlData)
     D  pDataLen                           value like(RXS_Length)


      //-----------------------------------------------------------------
      // @Author: 
      // @Created: 
      // @Desc:
      //-----------------------------------------------------------------
     P allHandler      b
     D allHandler      pi
     D  pType                              value like(RXS_Type)
     D  pXPath                             value like(RXS_XPath)
     D  pData                              value like(RXS_XmlData)
     D  pDataLen                           value like(RXS_Length)

     D chgMe           s                   like(RXS_XmlData)
     D baseEnv         s             70a   varying
      /free
        baseEnv = '/PostAdr';

        select;

        when pXPath = baseEnv +'/';
          chgMe = pData;
        when pXPath = baseEnv +'/name/';
          chgMe = pData;
        when pXPath = baseEnv +'/name/first/';
          chgMe = pData;
        when pXPath = baseEnv +'/name/last/';
          chgMe = pData;
        when pXPath = baseEnv +'/street/';
          chgMe = pData;
           ...
        endsl;
      /end-free
     P                 e
```

It's important to note that the code generated by BLDPRS will require some modifications to bring it in line with the new model for parsing subprocedures introduced in RXS3. The most important parts you'll need to replace are the procedure prototype and interface. Additionally, because the parsing subprocedure utilizes pointers instead of native character fields, the subprocedure [RXS_STR()](https://isupport.katointegrations.com/rxs/3.3/rxs_str.md) must be used to convert. Below is what the above sample would look like modified to be compatible with RXS3.

### IBM i 7.1+ Example Code

```rpgle
     D XmlHandler      PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value


      //-----------------------------------------------------------------
      // @Author: 
      // @Created: 
      // @Desc:
      //-----------------------------------------------------------------
     P XmlHandler      B
     D XmlHandler      PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D chgMe           S                   Like(RXS_Var1Kv_t)
     D baseEnv         S             70a   varying
      /free
        baseEnv = '/PostAdr';

        select;

        when pXPath = baseEnv +'/';
          chgMe = RXS_STR( pData : pDataLen );
        when pXPath = baseEnv +'/name/';
          chgMe = RXS_STR( pData : pDataLen );
        when pXPath = baseEnv +'/name/first/';
          chgMe = RXS_STR( pData : pDataLen );
        when pXPath = baseEnv +'/name/last/';
          chgMe = RXS_STR( pData : pDataLen );
        when pXPath = baseEnv +'/street/';
          chgMe = RXS_STR( pData : pDataLen );
           ...
        endsl;
      /end-free
     P                 E
```

### IBM i 6.1 Example Code

```rpgle
     D XmlHandler      PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value


      //-----------------------------------------------------------------
      // @Author: 
      // @Created: 
      // @Desc:
      //-----------------------------------------------------------------
     P XmlHandler      B
     D XmlHandler      PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D chgMe           S                   Like(RXS_Var1Kv_t)
     D baseEnv         S             70a   varying
      /free
        baseEnv = '/PostAdr';

        select;

        when pXPath = baseEnv +'/';
          RXS_STR( chgMe : pData : pDataLen );
        when pXPath = baseEnv +'/name/';
          RXS_STR( chgMe : pData : pDataLen );
        when pXPath = baseEnv +'/name/first/';
          RXS_STR( chgMe : pData : pDataLen );
        when pXPath = baseEnv +'/name/last/';
          RXS_STR( chgMe : pData : pDataLen );
        when pXPath = baseEnv +'/street/';
          RXS_STR( chgMe : pData : pDataLen );
           ...
        endsl;
      /end-free
     P                 E
```

---

# Offering a Web Service

> End-to-end tutorial for writing an RXS-hosted web service: parsing POST stdin data, composing an XML response with the template engine, and writing output with RXS_PutStdOut().

The following example program demonstrates how to offer a web service using RPG-XML Suite APIs. We will be receiving and transmitting XML data, and we will parse the request data using the DOM parser.

Throughout this tutorial, we will reference code samples. Some lines may have been omitted in these samples, and you should reference the [full program source code](#full-program-code) at the bottom of this page.

Here are the APIs we will be using:

| API Name | Description |
| --- | --- |
| [RXS_ResetDS()](https://isupport.katointegrations.com/rxs/3.3/rxs_resetds.md) | Resets and initializes RXS datastructures. |
| [RXS_GetStdIn()](https://isupport.katointegrations.com/rxs/3.3/rxs_getstdin.md) | Retrieves POST request data from stdin. |
| [RXS_OpenDom()](https://isupport.katointegrations.com/rxs/3.3/rxs_opendom.md) | Opens a DOM parsing session. |
| [RXS_XPath()](https://isupport.katointegrations.com/rxs/3.3/rxs_xpath.md) | Builds and formats an XPath for use in DOM parsing. |
| [RXS_ParseDomToText()](https://isupport.katointegrations.com/rxs/3.3/rxs_parsedomtotext.md) | Retrieves the data from the specified XPath. |
| [RXS_CloseDom()](https://isupport.katointegrations.com/rxs/3.3/rxs_closedom.md) | Ends a DOM parsing session and frees associated memory. |
| [RXS_StartComposeEngine()](https://isupport.katointegrations.com/rxs/3.3/rxs_startcomposeengine.md) | Opens an XML composition session and initializes the composition engine. |
| [RXS_ComposeSection()](https://isupport.katointegrations.com/rxs/3.3/rxs_composesection.md) | Writes a template section to the composition buffer. |
| [RXS_ComposeVariable()](https://isupport.katointegrations.com/rxs/3.3/rxs_composevariable.md) | Assigns a value to a template variable. |
| [RXS_GetComposeBuffer()](https://isupport.katointegrations.com/rxs/3.3/rxs_getcomposebuffer.md) | Retrieves the composed XML from the composition engine buffer. |
| [RXS_PutStdOut()](https://isupport.katointegrations.com/rxs/3.3/rxs_putstdout.md) | Sends response data to stdout. |
| [RXS_JobLog()](https://isupport.katointegrations.com/rxs/3.3/rxs_joblog.md) | Prints a formatted message to the job log. |

There are three main parts to a web service program:

1. [Parsing](#parsing) - retrieving data from the body of a POST request.
2. [Composing](#composing) - using a template member to compose response XML.
3. [Transmitting](#transmitting) - sending the HTTP response.

## Parsing 

When offering a web service, request data will be retrieved from stdin:

```rpgle
     gXmlRequest = RXS_GetStdIn();
```

Our gXmlRequest field now contains the XML content from the body of the POST request. We are expecting the following XML structure:

```xml
<GetOrdersRequest>
  <Customer>...</Customer>
</GetOrdersRequest>
```

We need to retrieve the data from our XML. Based on our expected request document structure, we will use the DOM parser to drill down to the specific target node and retrieve the raw data into gCUST:

```rpgle
     gRootDomDS = RXS_OpenDom( gXmlRequest );
     gXPath = RXS_XPath( '/GetOrdersRequest/Customer' );
     gCUST = RXS_ParseDomToText( gXPath : gRootDomDS);
```

## Composing 

In order to build our response XML, we need a compiled RXS template member. This example uses the following template file (built in the IFS using [BLDTPL](https://isupport.katointegrations.com/rxs/3.3/bldtpl.md)):

```xml
::GetOrders_Open
<GetOrdersResponse>
::Order_Line
  <Order>.:OrderNumber:.</Order>
::GetOrders_Close
</GetOrdersResponse>
```

which is compiled into a template source member using [CRTRPGTPL](https://isupport.katointegrations.com/rxs/3.3/creating_an_rxs_template.md). This template source member must be /COPY-ed into our program code twice. First, above the D-specs:

```rpgle
      /copy QRPGLETPL,GETORDS
```

and second, within our Template subprocedure:

```rpgle
     P Template        B
     D                 PI

     D  p                                  Like(RXS_TEMPLATE_PARM)

      /copy QRPGLETPL,GETORDS

     P                 E
```

The TemplateProcedure field in gComposeDS tells the composition engine where to find the Template subprocedure. We use a combination of RXS_ComposeSection() and RXS_ComposeVariable() to build the XML document:

```rpgle
     gComposeDS.TemplateProcedure = %paddr(Template);
     RXS_StartComposeEngine( gComposeDS );
     RXS_ComposeSection( GetOrders_Open );

     read QORDFILE;
     dou %eof(QORDFILE);
       if gCUST = CUST;
         RXS_ComposeVariable( OrderNumber : %trim(%editc(Order:'Z')) );
         RXS_ComposeSection( Order_Line );
       endif;
       read QORDFILE;
     enddo;

     RXS_ComposeSection( GetOrders_Close );
```

Variables within a section must be composed using RXS_ComposeVariable() before the section is composed with RXS_ComposeSection().

## Transmitting 

We will be returning our response data using stdout, as "text/xml" content. We are using RXS_GetComposeBuffer() to retrieve the composed XML data and release the associated memory:

```rpgle
     gPutStdOutDS.HeaderContentType = 'text/xml';
     RXS_PutStdOut( RXS_GetComposeBuffer() : gPutStdOutDS );
```

Once the response data is sent to stdout, your Apache instance will handle the rest of the communication.

## Full Program Code 

### QRPGLESRC/GETORDS.RPGLE

```rpgle
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')

     FQORDFILE  IF   E             DISK

      /copy QRPGLECPY,RXSCB                      RPG-XML Suite Prototypes & Definitions
      /copy QRPGLETPL,GETORDS


     D gComposeDS      DS                  LikeDS(RXS_ComposeDS_t)
     D gRootDomDS      DS                  LikeDS(RXS_ParseDomDS_t)
     D gXPath          S                   Like(RXS_Var8Kv_t)
     D gPutStdOutDS    DS                  LikeDS(RXS_PutStdOutDS_t)

     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
     D gCUST           S                   Like(CUST)

       /free

       *inlr = *on;

       monitor;

         gXmlRequest = RXS_GetStdIn();

         // Parse

         // <GetOrdersRequest>
         //   <Customer>1234</Customer>
         // </GetOrdersRequest>

         RXS_ResetDS( gRootDomDs : RXS_DS_TYPE_OPENDOM );
         gRootDomDS = RXS_OpenDom( gXmlRequest );
         gXPath = RXS_XPath( '/GetOrdersRequest/Customer' );
         gCUST = RXS_ParseDomToText( gXPath : gRootDomDS);
         RXS_CloseDom( gRootDomDS );

         // Compose

         RXS_ResetDS( gComposeDS : RXS_DS_TYPE_COMPOSE );
         gComposeDS.TemplateProcedure = %paddr(Template);
         RXS_StartComposeEngine( gComposeDS );

         RXS_ComposeSection( GetOrders_Open );

         read QORDFILE;
         dou %eof(QORDFILE);
           if gCUST = CUST;
             RXS_ComposeVariable( OrderNumber : %trim(%editc(Order:'Z')) );
             RXS_ComposeSection( Order_Line );
           endif;
           read QORDFILE;
         enddo;

         RXS_ComposeSection( GetOrders_Close );

         // Transmit

         RXS_ResetDS( gPutStdOutDS : RXS_DS_TYPE_PUTSTDOUT );
         gPutStdOutDS.HeaderContentType = 'text/xml';
         RXS_PutStdOut( RXS_GetComposeBuffer() : gPutStdOutDS );

       on-error;

         RXS_Joblog('Unexpected error occurred. See previous messages.');

       endmon;

      /end-free

     P Template        B
     D                 PI

     D  p                                  Like(RXS_TEMPLATE_PARM)

      /copy QRPGLETPL,GETORDS

     P                 E
```

### QRPGLETPL/GETORDS.RPGLE

```rpgle
      /IF NOT DEFINED(GETORDS)                                            
      /DEFINE GETORDS                                                     
     D  GetOrders_Open...                                                 
     D                 C                   'GetOrders_Open'               
     D  Order_Line...                                                     
     D                 C                   'Order_Line'                   
     D  GetOrders_Close...                                                
     D                 C                   'GetOrders_Close'              
     D  OrderNumber...                                                    
     D                 C                   'OrderNumber'                  
      /ELSE                                                               
      /free                                                               
       p  = '';                                                           
       p += '::GetOrders_Open' + x'15';                                   
       p += '<GetOrdersResponse>' + x'15';                                
       p += '::Order_Line' + x'15';                                       
       p += '  <Order>' + '.:OrderNumber:.' + '</Order>' + x'15';         
       p += '::GetOrders_Close' + x'15';                                  
       p += '</GetOrdersResponse>' + x'15';  

      /end-free 
      /ENDIF
```

---

# Configuring RXS Router

> Two-stage guide to configuring Apache httpd.conf for RXS CGI routing and setting up static or dynamic RXSRTR routing entries.

There are two main stages to routing configuration when using RXS Router. The first happens through the Apache configuration file - httpd.conf - which is typically in an IFS path like `/www/rxs/conf/httpd.conf` or `/www/myrxs/conf/httpd.conf`. The second stage happens within RXS Router and the RXS Router environments on your system.

## Apache Configuration
RXS Router determines which environment to load based on the value of the RXSRTR_ENVIRONMENT variable, which is set by Apache. The first step is to add the following directive to your httpd.conf file:

```apache
ScriptAliasMatch ^/(([^/.]+/)*[^/.]+)$ /qsys.lib/RXS.lib/rxsrtr.pgm
<Directory /qsys.lib/RXS.lib/>
  Options +ExecCGI
  Allow From all
</Directory>
```

This directive tells Apache to call RXSRTR for every URL on which it is listening. This will allow Apache to transfer control to RXS Router, which will then run the associated programs before returning any output to Apache.

RXS Router checks the RXSRTR_ENVIRONMENT variable to determine which configuration should be loaded for a given request. This environment variable value is set by Apache. For each path which has a corresponding RXS Router environment, a Location element must be added to httpd.conf:

```apache
<Location /myrxs/>
  SetEnv RXSRTR_ENVIRONMENT 'MYRXS'
</Location>
```

The value of '/myrxs/' should be substituted by your desired URL path, and the value of 'MYRXS' should correspond to the RXS Router environment that should be loaded.

## RXS Router Configuration
The configuration of your RXS Router environment depends on which type of routing you are using - static or dynamic. Configuration records are stored in control files, which are accessible with the [WRKRXSRTRE](https://isupport.katointegrations.com/rxs/3.3/wrkrxsrtre.md) command.

### Routing

#### Static (environment variable) routing
If the **routingid flag** has a value which begins with `ENVVAR:`, RXSRTR will check the specified environment variable on each request to determine the RRID. For instance, if the routingid flag is specified as follows:

`-routingid envvar:http_soapaction`

then whenever RXSRTR is called by the HTTP server, it will retrieve the value of the HTTP_SOAPACTION environment variable and will use that value as the RRID.

This processing will be performed for every request handled by RXSRTR, irrespective of the actual URL used.

If the specified environment variable is a path name, you may specify a qualifier of a colon followed by a slash, immediately following the environment variable name, e.g.:

`-routingid envvar:http_soapaction:/`

and RXSRTR will use only the last path segment of the environment variable as the RRID. For instance, if the value of the HTTP_SOAPACTION variable is

`https://www.mycompany.com/webservices/local/values/focus/getasset`

then the RRID used would be 'GETASSET'.

#### Dynamic routing
If the **routingid** flag has a value which does not begin with `ENVVAR:`, RXSRTR will use 'dynamic' routing, whereby the actual URL used by the client will be parsed to determine the RRID.

RXSRTR uses the following steps to determine the RRID from the URL (as soon as the RRID has been determined, further steps are bypassed):

1. The value of the QUERY_STRING environment variable is retrieved as {querystring}
2. The SCRIPT_NAME environment variable is retrieved as {scriptname} and parsed into its constituent 'segments'. For instance, a SCRIPT_NAME value of /rxsrtr/petstore/item' is parsed into 3 separate segments - 'rxsrtr', 'petstore' and 'item'. All values are converted to upper-case for comparison purposes during parsing.
3. If one of the {scriptname} segments has a value of routingid and the next segment is not blank, that next segment is used as the RRID.
4. If {querystring} is blank, the first {scriptname} segment (retrieved in step 2) is used as the RRID.
5. If one of the variables in {querystring} is routingid, its value is used as the RRID.
6. Otherwise the first {scriptname} segment is used as the RRID.

This means that all of the following example URI's will result in an RRID of 'MICKEY' (assuming that the value of the routingid flag has the default value of 'RXSRTR')

7. `http://192.168.0.1/rxsrtr/mickey/foo/bar`
8. `http://192.168.0.1/mickey/foo/bar`
9. `http://192.168.0.1/foo/bar?fname=john&rxsrtr=mickey&lname=smith`
10. `http://192.168.0.1/mickey/foo/bar?fname=john&lname=smith`

In case 1, RXSRTR is specified as a path segment and the subsequent path segment is not blank, so it is used as the RRID. In case 2, RXSRTR is not specified as any of the path segments, and the QUERY_STRING variable is blank, so the first path segment is used as the RRID. In case 3, RXSRTR is not specified as any of the path segments. However, the QUERY_STRING variable does contain a variable called RXSRTR, so that variable's value is used as the RRID. In case 4, RXSRTR is not specified as any of the path segments, and the none of the QUERY_STRING variables is called 'RXSRTR', so the first path segment is used as the RRID.

### RXSRTRCTL Entries
Configuration entries for RXS Router are managed using [WRKRXSRTRE](https://isupport.katointegrations.com/rxs/3.3/wrkrxsrtre.md). For a given RXS Router control file, there will be two or more individual entries, which are used to determine what programs are called by RXS Router.

Each control file must contain a \*CONFIG entry. This entry is loaded first by RXS Router, and is used to determine which record in the environment is loaded for the final routing stage. More information about the options used by \*CONFIG can be found on the [RXS Router](https://isupport.katointegrations.com/rxs/3.3/rxsrtr.md) page.

The default value for the routing ID is **RXSRTR**, which instructs RXS Router to use dynamic routing to determine which control file record to load, based on the URL. For static routing, RXS Router will refer to the value of the specified environment variable to determine the correct control file record, and the routing ID flag must be specified using the `ENVVAR:` prefix.

Subsequent entries within your RXS Router control file contain configuration information for calling a specific program. RXS Router determines which of these records to invoke using the criteria outlined in the [Routing](#routing) section. There are many flags that can be specified for a given routing ID, but the only required one is `-pgm`, which tells RXS Router which program should be called.

The `-pgm` flag can either contain the name of a program or a special value (\*0-\*9). If a name is specified, RXS Router will search through the library list of the job for a match, or look in the library specified in the `-lib` tag. If a special value is used, RXS Router will refer to the URL path to determine which program should be called.

#### With Name
The following configuration will run the program DEMOSVC in the library MYRXS:

`-pgm DEMOSVC -lib MYRXS`

#### With Special Value
The following configuration will dynamically determine the program to be called, based on the URL, within the library list of the job decription (\*JOBD) specified by the `-liblst` flag:

`-pgm *1 -liblst MYRXS/RTRJOB`

The special value of \*0 corresponds to the routing ID, while \*1-\*9 represent URL path segments. If the service was called using the following URL:

`https:/api.myservice.com/myrxs/demosvc/`

the routing ID will be MYRXS, and RXS Router will call the program DEMOSVC.

---

# CELSIUS

> RPG example composing an XML request, calling a remote web service, and parsing the XML response with the event-based parser.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to compose an XML request, call a remote web service, and parse the response XML with event-based parsing.

## CELSIUS Program 

```rpgle
     H DEBUG(*YES) DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

      /DEFINE RXSV6R1

      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,CELSIUS

     D Template        PR
     D  p                                  Like(RXS_TEMPLATE_PARM)

     D XmlHandler      PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D gFahrenheit     S             10I 0
     D gCelsius        S             10I 0
     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
     D gXmlResponse    S                   Like(RXS_Var64Kv_t)


     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D ParseDS         DS                  LikeDS(RXS_ParseDS_t)
     D ErrorDS         DS                  LikeDS(RXS_CatchThrowErrorDS_t)

      /free

        *Inlr = *On;

        monitor;

          gFahrenheit = 100;

          exsr compose;

          exsr transmit;

          exsr parse;

          RXS_ResetDS( ErrorDS : RXS_DS_TYPE_CATCHTHROWERROR );
          ErrorDS.MessageId = 'RXS9897';
          ErrorDS.MessageData = 'Celsius Temp: '
            + %trim ( %editc( gCelsius : '3' ) + '&deg;');
          ErrorDS.ThrowToCaller = RXS_YES;
          RXS_Throw( ErrorDS );
        on-error;

        endmon;


        begsr compose;

          RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
          ComposeDS.TemplateProcedure = %paddr(Template);
          RXS_StartComposeEngine(ComposeDS);

          RXS_ComposeVariable( fahrenheit : %Char(gFahrenheit) );
          RXS_ComposeSection( content );

          RXS_GetComposeBuffer(gXmlRequest);

        endsr;


        begsr transmit;

          RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
          TransmitDS.URI =
            'http://www.w3schools.com/webservices/tempconvert.asmx';
          TransmitDS.HeaderSOAPAction =
            '&quot;http://www.w3schools.com/webservices/FahrenheitToCelsius&quot;';
          TransmitDS.LogFile = '/tmp/celsius.txt';
          TransmitDS.HeaderContentType = 'text/xml; charset=utf-8';
          TransmitDS.RequestCcsid = RXS_CCSID_UTF8;
          RXS_Transmit( gXmlResponse : gXmlRequest : TransmitDS );

        endsr;

        begsr parse;

          RXS_ResetDS( ParseDS : RXS_DS_TYPE_PARSE );
          ParseDS.GlobalHandler = %Paddr( XmlHandler );
          RXS_Parse( gXmlResponse : ParseDS );

        endsr;

      /end-free

     P XmlHandler      B
     D XmlHandler      PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value
     D
     D parsed          S                   Like(RXS_Var1Kv_t)
      /free

        if pXPath =
           '/Envelope/Body/FahrenheitToCelsiusResponse' +
           '/FahrenheitToCelsiusResult/';

          // Calling RXS_STR in the V6R1-style forces you to use an
          //  intermediate variable when you need to convert type to
          //  integer or decimal. In V7R1-style, the below two lines of
          //  code would be a single line that looks like this:
          //      gCelsius = %Int( RXS_STR( pData : pDataLen ) );
          RXS_STR( parsed : pData : pDataLen );
          gCelsius = %Int( parsed );
        endif;

      /end-free
     P                 E

     P Template        B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)

       // Template RPG source was created from the actual template
       //  STMF using the following command:

       //     CRTRPGTPL STMF('/www/rxs/templates/geturi2.tpl')
       //       FILE(RXS3/QRPGLETPL) MBR(CELSIUS)

       // The RPG Template source is copied from QRPGLETPL by using
       //  /copy once in the D-specs and again below.

      /copy QRPGLETPL,CELSIUS

     P                 E
```

## CELSIUS Template 

```rpgle
      /IF NOT DEFINED(CELSIUS)
      /DEFINE CELSIUS
     D
     D                 DS
     D  CONTENT...
     D                               50A   Varying
     D
     D                 DS
     D  fahrenheit...
     D                               30A   Varying
      /EOF
      /ENDIF
      /free

        CONTENT =
          'CONTENT';
        fahrenheit =
          'fahrenheit';

       p  = '';
       p += '::CONTENT' + x'15';
       p += '<?xml version="1.0" encoding="utf-8"?>' + x'15';
       p += '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-i';
       p += 'nstance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:';
       p += 'soap="http://schemas.xmlsoap.org/soap/envelope/">' + x'15';
       p += '  <soap:Body>' + x'15';
       p += '    <FahrenheitToCelsius xmlns="https://www.w3schools.com/';
       p += 'xml/">' + x'15';
       p += '      <Fahrenheit>' + '.:fahrenheit:.' + '</Fahrenheit>' + x'15';
       p += '    </FahrenheitToCelsius>' + x'15';
       p += '  </soap:Body>' + x'15';
       p += '</soap:Envelope>' + x'15';

      /end-free
```

---

# CELSIUSDOM

> RPG example composing an XML request, calling a remote web service, and parsing the XML response with the DOM parser.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to compose an XML request, call a remote web service, and parse the response XML with the DOM parser.

```rpgle
     H DEBUG(*YES) DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)
      /copy QRPGLECPY,RXSCB
      /copy QRPGLETPL,CELSIUS

     D Template        PR
     D  p                                  Like(RXS_TEMPLATE_PARM)


     D gData           S                   Like(RXS_Var1Kv_t)
     D gCelsius        S             10I 0
     D gFahrenheit     S             10I 0
     D gXmlRequest     S                   Like(RXS_Var64Kv_t)
     D gXmlResponse    S                   Like(RXS_Var64Kv_t)
     D gXPath          S                   Like(RXS_Var8Kv_t)


     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D RootDomDS       DS                  LikeDS(RXS_ParseDOMDS_t)
     D ErrorDS         DS                  LikeDS(RXS_CatchThrowErrorDS_t)
      /free

       monitor;

         gFahrenheit = 100;

         exsr compose;

         exsr transmit;

         exsr parse;

         RXS_ResetDS( ErrorDS : RXS_DS_TYPE_CATCHTHROWERROR );
         ErrorDS.MessageId = 'RXS9897';
         ErrorDS.MessageData = 'Celsius Temp: '
           + %trim ( %editc( gCelsius : '3' ) + '&deg;');
         ErrorDS.ThrowToCaller = RXS_YES;
         RXS_Throw( ErrorDS );
       on-error;

       endmon;

       *Inlr = *On;

       begsr compose;

         RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
         ComposeDS.TemplateProcedure = %Paddr( Template );
         RXS_StartComposeEngine( ComposeDS );
         RXS_ComposeVariable( fahrenheit : %Char(gFahrenheit) );
         RXS_ComposeSection( content );
         gXmlRequest =  RXS_GetComposeBuffer();
       
       endsr;

       begsr transmit;

         RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
         TransmitDS.URI =
           'http://www.w3schools.com/xml/tempconvert.asmx';
         TransmitDS.HeaderSOAPAction =
           '&quot;http://www.w3schools.com/xml/FahrenheitToCelsius&quot;';
         TransmitDS.LogFile = '/tmp/celsius.txt';
         TransmitDS.HeaderContentType = 'text/xml; charset=utf-8';
         TransmitDS.RequestCcsid = RXS_CCSID_UTF8;
         gXmlResponse = RXS_Transmit( gXmlRequest : TransmitDS );
       
       endsr;

       begsr parse;

         RootDomDS = RXS_OpenDom( gXmlResponse );
         gXPath =
           RXS_XPath('/*:Envelope/*:Body/*:FahrenheitToCelsiusResponse' +
                     '/*:FahrenheitToCelsiusResult' );
         gData = RXS_ParseDomToText( gXPath : RootDomDS );
         RXS_CloseDom( RootDomDS );
         gCelsius = %Int( gData );
       
       endsr;
       
      /end-free
     P Template        B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)
        // Template RPG source was created from the actual template
        //  STMF using the following command:
        //     CRTRPGTPL STMF('/www/rxs/templates/geturi2.tpl')
        //       FILE(RXS3/QRPGLETPL) MBR(CELSIUS)
        // The RPG Template source is copied from QRPGLETPL by using
        //  /copy once in the D-specs and again below.
      /copy QRPGLETPL,CELSIUS
     P                 E
```

---

# DOMRECUR

> RPG example using the DOM parser to process an XML document containing multiple repeating elements.

This example program demonstrates the basic structure of an RPG program utilizing the RPG-XML Suite DOM parser to parse an XML document with multiple repeating elements.

```rpgle
     H BNDDIR('RXSBND') DFTACTGRP(*NO) ACTGRP(*CALLER)
      /copy qrpglecpy,rxscb
     D gRootDomDS      DS                  LikeDS(RXS_ParseDomDS_t) Inz
     D gBookDomDS      DS                  LikeDS(RXS_ParseDomDS_t) Inz
     D gReviewDomDS    DS                  LikeDS(RXS_ParseDomDS_t) Inz
     D gXml            S          65535A   Varying(4)
     D gData           S          65535A   Varying(4)
     D gXPath          S                   Like(RXS_Var8Kv_t)
     D x               S             10U 0
     D y               S             10U 0
      /free
       monitor;

         gXml = '<?xml version="1.0" encoding="UTF-8"?>' +
           '<bookstore xmlns:test="testnamespace">' +
           '<book category="children">' +
           '<title lang="en"><![CDATA[Charlotte''s Web]]></title>' +
           '<author>E. B. White</author>' +
           '<year>1952</year>' +
           '<price>5.99</price>' +
           '<review><text>What a great book!</text></review>' +
           '<review><text>Highly recommended.</text></review>' +
           '</book>' +
           '<book category="cooking">' +
           '<title lang="en">Everyday Italian</title>' +
           '<author>Giada De Laurentiis</author>' +
           '<year>2005</year>' +
           '<price>30.00</price>' +
           '</book>' +
           '<book category="science fiction">' +
           '<title lang="en">Gideon the Ninth</title>' +
           '<author>Tamsyn Muir</author>' +
           '<year>2019</year>' +
           '<price>19.99</price>' +
           '</book>' +
           '</bookstore>';

         gRootDomDS = RXS_OpenDom(  gXml );
         gXPath = RXS_XPath( '/*:bookstore/*:book' );
         gBookDomDS = RXS_ParseDomToDom( gXPath : gRootDomDS );

         for x = 1 to gBookDomDS.NodeCount;

           gXPath = RXS_XPath( '*:book[%u]/*:title' : x );
           gData = RXS_ParseDomToText( gXPath : gBookDomDS );
           RXS_JobLog( 'Title: %s' : gData );
           gXPath = RXS_XPath( '*:book[%u]/*:review' : x );
           gReviewDomDS = RXS_ParseDomToDom( gXPath : gBookDomDS );

           for y = 1 to gReviewDomDS.NodeCount;

             gXPath = RXS_XPath( '*:review[%u]/*:text' : y );
             gData = RXS_ParseDomToText( gXPath : gReviewDomDS );
             RXS_JobLog( 'Review: %s' : gData );

           endfor;

         endfor;

         RXS_CloseDom( gRootDomDS );

       on-error;

       endmon;

       *INLR = *ON;

      /end-free
```

---

# EXCOMPOSE1

> RPG example composing a JSON document with repeating array elements using the JSON composition subprocedures.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to compose a JSON string with repeating elements.

```rpgle
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')
      /copy QRPGLECPY,RXSCB
     D PutStmfDS       DS                  LikeDS(RXS_PutStmfDS_t)
     D CreateDS        DS                  LikeDS(RXS_CreateJsonDS_t)
     D RootDS          DS                  LikeDS(RXS_JsonStructureDS_t)
     D UsersArrayDS    DS                  LikeDS(RXS_JsonStructureDS_t)
     D UserObjectDS    DS                  LikeDS(RXS_JsonStructureDS_t)
     D GenericArrayDS  DS                  LikeDS(RXS_JsonStructureDS_t)
     D i               S              3U 0 Inz(0)
     D Json            S                   Like(RXS_Var64Kv_t)
     D dateVal         S             10A
      /FREE

       monitor;

         // Use RXS_ResetDS to ensure that RXS templated data structures are 
         //  properly configured
         RXS_ResetDS( CreateDS : RXS_DS_TYPE_CREATEJSON );
         RXS_ResetDS( RootDS : RXS_DS_TYPE_JSONSTRUCTURE );
         RXS_ResetDS( UsersArrayDS : RXS_DS_TYPE_JSONSTRUCTURE );
         RXS_ResetDS( UserObjectDS : RXS_DS_TYPE_JSONSTRUCTURE );
         RXS_ResetDS( GenericArrayDS : RXS_DS_TYPE_JSONSTRUCTURE );

         // Create root JSON Object
         CreateDS.Prettify = RXS_JSON_TRUE;
         CreateDS.JsonStructureType = RXS_JSON_STRUCTURE_OBJECT;
         RootDS = RXS_CreateJson( CreateDS );

         // Create JSON Array named users & attach to RootDS
         UsersArrayDS = RXS_ComposeJsonArray( 'users' : RootDS );

         // Loop through and add multiple objects to the JSON Array named
         //  "users". Typically you'd want to use a normal RPG database
         //  read loop here instead of a 'for' loop.
         for i = 1 to 3;

           // Create object for a customer, attach to array
           UserObjectDS = RXS_ComposeJsonObject( *OMIT : UsersArrayDS );

           // Add specific string fields & data to the User object we just
           //  created.
           RXS_ComposeJsonString( 'firstName' : 'First Name' : UserObjectDS );
           RXS_ComposeJsonString( 'lastName' : 'Last Name' : UserObjectDS );
           RXS_ComposeJsonString( 'email' : 'Email Address' : UserObjectDS );
           RXS_ComposeJsonString( 'username' : 'Username' : UserObjectDS );
           RXS_ComposeJsonString( 'password' : 'Password' : UserObjectDS );
         
         endfor;

         // The next few parts of this JSON structure are a series of arrays.
         //  We could define D spec data structure for each if we wanted to,
         //  but it's not required. We still need to ensure we're attaching
         //  them to our root object RootDS though.
         // requiredCourseIds[] - Mindflash says optional
         GenericArrayDS =
           RXS_ComposeJsonArray( 'requiredCourseIds' : RootDS );

         // Again, you'd probably be reading one or more child records out of
         //  a physical file here with an RPG read loop instead of a
         //  'for' loop.
         for i = 1 to 10;

           // Note that the key difference between a JSON Object and a
           //  JSON Array is that a JSON Object can contain one or more
           //  name/value pairs (and the values can be of any type - a child
           //  JSON Object, a JSON Array, or just a plain string/int/etc value)
           // A JSON Array can hold one or more values ONLY. The values a JSON
           //  Array holds must all be of the same type (e.g. all JSON Object,
           //  all plain string/int/etc values, or even another JSON Array),
           //  and cannot have a name associated with them.
           // The easiest way to think about these two is that a JSON Object
           //  is like an RPG data structure, whereas a JSON Array is like
           //  an RPG data structure or normal field but with the Dim keyword
           //  used.
           // Because we can't name the values we're putting in our array, the
           //  second parm for RXS_ComposeJsonNumber() must be *OMIT.
           // We're using RXS_ComposeJsonNumber() to ensure that in the JSON
           //  the values we're passing show up as numeric (e.g. not wrapped
           //  in double quotes)
           // To handle large numbers effectively, RXS_ComposeJsonNumber()
           //  actually accepts a character representation of a number as
           //  opposed to an RPG numeric field type.
           RXS_ComposeJsonNumber( *OMIT : %Char(i) : GenericArrayDS );

         endfor;
         
         // We're re-using the GenericArrayDS structure for the next few arrays
         //  as they are very simple arrays but the whole process is otherwise
         //  the same as for requiredCourseIds[]
         GenericArrayDS = RXS_ComposeJsonArray( 'courseIds' : RootDS );

         for i = 1 to 10;
           RXS_ComposeJsonNumber( *OMIT : %Char(i * 2) : GenericArrayDS );
         endfor;

         GenericArrayDS = RXS_ComposeJsonArray( 'seriesIds' : RootDS );

         for i = 1 to 10;
           RXS_ComposeJsonNumber( *OMIT : %Char(i * 3) : GenericArrayDS );
         endfor;

         GenericArrayDS = RXS_ComposeJsonArray( 'groupIds' : RootDS );

         for i = 1 to 10;
           RXS_ComposeJsonNumber( *OMIT : %Char(i * 4) : GenericArrayDS );
         endfor;

         // Next, we need to add a date named clientDatestamp to our RootDS.
         //  JSON doesn't have a 'date' data type, so we add this as a
         //  string and do whatever formatting the API we're calling requires.
         // In this case we're using a YYYY-MM-DD date format
         dateVal = %Char( %Date() : *ISO );
         RXS_ComposeJsonString( 'datestamp' : dateVal : RootDS );
         
         // Next we're going to add JSON boolean and null values
         RXS_ComposeJsonBoolean( 'processed' : RXS_JSON_TRUE : RootDS );
         RXS_ComposeJsonNull( 'referenceId' : RootDS );
         
         // Now that we've added all of the data fields to our JSON object,
         //  let's generate our JSON string
         Json = RXS_GetJsonString( CreateDS );
         
         // Always call RXS_JsonDestroy() to free up the memory we used.
         // This must be called AFTER we retrieve our string0
         //  with RXS_JsonGetString(), or there will be nothing to retrieve!
         // It is enough to just call RXS_JsonDestroy() on the CreateDS data
         //  structure - it will free the memory used by all of the child
         //  data structures that were linked to it as well.
         RXS_DestroyJson( CreateDS );
         
         // Dump JSON to file
         RXS_ResetDS( PutStmfDS : RXS_DS_TYPE_PUTSTMF );
         PutStmfDS.Stmf = '/tmp/json_excompose1.txt';
         RXS_PutStmf( Json : PutStmfDS );

       on-error;

         RXS_DestroyJson( CreateDS );
       
       endmon;
       
       RXS_DestroyJson( CreateDS );
       
       *INLR = *ON;
       
       return;
       
      /END-FREE
```

---

# EXPARSE1

> RPG example parsing a JSON response using an event handler subprocedure.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to parse JSON data.

```rpgle
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')
      /copy QRPGLECPY,RXSCB
     D JSONHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
     D Json            S                   Like(RXS_Var64Kv_t)
     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
      /FREE

       Json = '{ &quot;glossary&quot;: { &quot;title&quot;:'
            + ' &quot;example glossary&quot;, &quot;GlossDiv&quot;: { &quot;title&quot;: &quot;S&quot;, &quot;'
            + 'GlossList&quot;: [ { &quot;ID&quot;: &quot;SGML&quot;, &quot;SortAs&quot;: &quot;SGML&quot;, &quot;'
            + 'GlossTerm&quot;: &quot;Standard Generalized Markup Language&quot;, &quot;Ac'
            + 'ronym&quot;: &quot;SGML&quot;, &quot;Abbrev&quot;: &quot;ISO 8879:1986&quot;, &quot;GlossDe'
            + 'f&quot;: &quot;A meta-markup language, used to create markup langua'
            + 'ges such as DocBook.&quot;, &quot;GlossSeeAlso&quot;: [&quot;GML&quot;, &quot;XML&quot;'
            + ', &quot;markup&quot;, &quot;test&quot;] } ] } } }';

       monitor;
         // Use RXS_ResetDS to ensure that RXS templated data structures are
         //  properly configured before use
         RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );

         // The JSON parsing in RXS functions via an &quot;event-based&quot; method.
         //  Basically, you will create a &quot;handler&quot; subprocedure that will
         //  be called by RXS_ParseJson once for each &quot;event&quot; detected in
         //  the JSON being parsed. An event is one of the following:
         //  - beginning of an JSON Array (RXS_JSON_ARRAY_BEGIN)
         //  - end of an JSON Array (RXS_JSON_ARRAY_END)
         //  - beginning of an JSON Object (RXS_JSON_OBJECT_BEGIN)
         //  - end of an JSON Object (RXS_JSON_OBJECT_END)
         //  - a JSON string value (RXS_JSON_STRING)
         //  - a JSON integer value (RXS_JSON_INTEGER)
         //  - a JSON double (float) value (RXS_JSON_DOUBLE)
         //  - a JSON boolean (indicator) value (RXS_JSON_BOOLEAN)
         //  - a JSON null value (RXS_JSON_NULL)
         // Inside the &quot;handler&quot; subprocedure (named JSONHandler here), you
         //  will receive a number of parameters that describe the data
         //  associated with the event detected. JSONHandler() explains this
         //  in more detail
         ParseJsonDS.Handler = %Paddr( JSONHandler );
         RXS_ParseJson( Json : ParseJsonDS );

       on-error;
       endmon;

       *INLR = *ON;

       return;

      /END-FREE


     P JSONHandler     B                   Export
     D                 PI              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const
      *
     D ParsedData      S                   Like(RXS_Var1Kv_t)
      /FREE

       // JSONHandler must always be specified with the exact same prototype
       //  - the same number of parameters, the same number of field types, in
       //  the same order, etc. The parameters are:
       //
       // - pType = the event type, e.g. RXS_JSON_STRING, RXS_JSON_ARRAY_BEGIN,
       //    etc.
       // - pPath = XPath like syntax that indicates where in the JSON
       //    structure being parsed we are located. You can &quot;drill down&quot; with
       //    slashes (e.g. /object1/field1 corresponds to a 'field1' inside
       //    a parent object named 'object1', and you can indicate when a JSON
       //    JSON Array is present via [*] - further explanation below.
       // -pIndex = If the JSON object/event we're handling is a child of a
       //    JSON Array, this will indicate the 'index&quot;, e.g. the position of
       //    this object within the array, starting from a value of 1. This
       //    parameter is not relevant for objects/events not inside an array
       // -pData = Pointer to the actual data returned. This will only be set
       //    for RXS_JSON_STRING, RXS_JSON_INTEGER, RXS_JSON_DOUBLE, and
       //    RXS_JSON_BOOLEAN. It is technically also set for RXS_JSON_NULL,
       //    but will be an RPG *Null value. For RXS_JSON_OBJECT,
       //    RXS_JSON_OBJECT_END, RXS_JSON_ARRAY, and RXS_JSON_ARRAY_END,
       //    this will also be *Null as there isn't any data associated with
       //    these events.
       // -pDataLen = Length of the object. Typically this is used as the
       //    length of pData as a call to RXS_STR() as seen below, but pDataLen
       //    is also set to either the total length of a JSON Array or the
       //    number of &quot;first tier&quot; fields in a JSON Object

       // We're going to grab the content of the JSON object that was passed
       //  to JSONHandler, assuming that the object HAS content, e.g. it is
       //  a string, integer, double, or boolean.
       if pData <> *Null and pDataLen > 0;
         ParsedData = RXS_STR( pData : pDataLen );
       endif;

       select;
         // For JSON parsing with RXS, a [*] being part of the path indicates
         //  that an array is involved. If you need to know the specific
         //  position in the array you can look at the pIndex value. Note that
         //  for nested arrays pIndex will show the position of the deepest
         //  JSON Array in the path.

         when pPath = '/';
           RXS_JobLog( 'Root object (length %s)' : %Char(pDataLen) );

         when pPath = '/glossary';
           RXS_JobLog( 'glossary object (length %s)' : %Char(pDataLen) );

         when pPath = '/glossary/title';
           RXS_JobLog( 'glossary title: %s' : ParsedData );

         when pPath = '/glossary/GlossDiv/title';
           RXS_JobLog( 'GlossDiv title: %s' : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]';
           if pType = RXS_JSON_ARRAY;
             RXS_JobLog( 'Begin GlossList Array (length %s)'
                       : %Char(pDataLen) );
           elseif pType = RXS_JSON_ARRAY_END;
             RXS_JobLog( 'End GlossList Array (length %s)'
                       : %Char(pDataLen) );
           endif;

         when pPath = '/glossary/GlossDiv/GlossList[*]/ID';
           RXS_JobLog( 'GlossList Array#%s ID: %s'
                     : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/SortAs';
           RXS_JobLog( 'GlossList Array#%s SortAs: %s'
                     : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossTerm';
           RXS_JobLog( 'GlossList Array#%s GlossTerm: %s'
                     : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/Acronym';
           RXS_JobLog( 'GlossList Array#%s Acronym: %s'
                     : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/Abbrev';
           RXS_JobLog( 'GlossList Array#%s Abbrev: %s'
                     : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossDef';
           RXS_JobLog( 'GlossList Array#%s GlossDef: %s'
                     : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]';
           if pType = RXS_JSON_ARRAY;
             RXS_JobLog( 'Begin GlossSeeAlso Array (length %s)'
                       : %Char(pDataLen) );
           elseif pType = RXS_JSON_ARRAY_END;
             RXS_JobLog( 'End GlossSeeAlso Array (length %s)'
                       : %Char(pDataLen) );
           else;
             RXS_JobLog( 'GlossSeeAlso Array#%s value: %s'
                       : %Char(pIndex) : ParsedData );
           endif;

       endsl;

       // If you want to stop parsing early for whatever reason, return
       // RXS_JSON_STOP_PARSING
       return RXS_JSON_CONTINUE_PARSING;

      /END-FREE
     P                 E
```

---

# XSDVALID

> RPG example validating an XML document against an XSD schema, demonstrating both passing and failing validation scenarios.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to validate sample XML against an XSD. Several methods are demonstrated, with both pass and fail outcomes.

```rpgle
     H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)
      /copy QRPGLECPY,RXSCB
     D TransmitDS      DS                  LikeDS(RXS_TransmitDS_t)
     D XsdValidateDS   DS                  LikeDS(RXS_XsdValidationDS_t)
     D PutStmfDS       DS                  LikeDS(RXS_PutStmfDS_t)
     D DeleteStmfDS    DS                  LikeDS(RXS_DeleteStmfDS_t)
     D request         S                   Like(RXS_Var64Kv_t)
     D goodXml         S                   Like(RXS_Var64Kv_t)
     D badXml          S                   Like(RXS_Var64Kv_t)
     D xsd             S                   Like(RXS_Var64Kv_t)
     D i               S             10I 0
      /FREE

       exsr Setup;

       // Test validation against URI (Expected Success)
       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdLocation =
         'http://files.rpg-xml.com/example/xsdvalidate/validate.xsd';

       if RXS_Validate( goodXml : XsdValidateDS );
         RXS_JobLog( 'XML 1 Validated successfully');
       else;
         RXS_JobLog( 'XML 1 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s'
                   : %Char( XsdValidateDS.ValidationErrorCode )
                   : XsdValidateDS.ValidationErrorText );
       endif;

       // Test validation against XSD in a field (Expected Success)
       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdPointer = %Addr( xsd : *DATA );
       XsdValidateDS.XsdLength = %Len( xsd );

       if RXS_Validate( goodXml : XsdValidateDS );
         RXS_JobLog( 'XML 2 Validated successfully');
       else;
         RXS_JobLog( 'XML 2 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s'
                   : %Char( XsdValidateDS.ValidationErrorCode )
                   : XsdValidateDS.ValidationErrorText );
       endif;

       // Test validation against XSD in a file (Expected Success)
       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdLocation = PutStmfDS.Stmf;

       if RXS_Validate( goodXml : XsdValidateDS );
         RXS_JobLog( 'XML 3 Validated successfully');
       else;
         RXS_JobLog( 'XML 3 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s'
                   : %Char( XsdValidateDS.ValidationErrorCode )
                   : XsdValidateDS.ValidationErrorText );
       endif;

       // Test validation against URI (Expected Failure)
       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdLocation =
         'http://files.rpg-xml.com/example/xsdvalidate/validate.xsd';

       if RXS_Validate( badXml : XsdValidateDS );
         RXS_JobLog( 'XML 4 Validated successfully');
       else;
         RXS_JobLog( 'XML 4 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s'
                   : %Char( XsdValidateDS.ValidationErrorCode )
                   : XsdValidateDS.ValidationErrorText );
       endif;

       // Test validation against XSD in a field (Expected Failure)
       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdPointer = %Addr( xsd : *DATA );
       XsdValidateDS.XsdLength = %Len( xsd );

       if RXS_Validate( badXml : XsdValidateDS );
         RXS_JobLog( 'XML 5 Validated successfully');
       else;
         RXS_JobLog( 'XML 5 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s'
                   : %Char( XsdValidateDS.ValidationErrorCode )
                   : XsdValidateDS.ValidationErrorText );
       endif;

       // Test validation against XSD in a file (Expected Failure)
       RXS_ResetDS( XsdValidateDS : RXS_DS_TYPE_XSDVALIDATION );
       XsdValidateDS.XsdLocation = PutStmfDS.Stmf;

       if RXS_Validate( badXml : XsdValidateDS );
         RXS_JobLog( 'XML 6 Validated successfully');
       else;
         RXS_JobLog( 'XML 6 Validation failed');
         RXS_JobLog( 'Validation Error #%s: %s'
                   : %Char( XsdValidateDS.ValidationErrorCode )
                   : XsdValidateDS.ValidationErrorText );
       endif;

       exsr Cleanup;

       *INLR = *ON;

       begsr Setup;

         // Get good XML to test validation
         RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
         TransmitDS.Uri =
           'http://files.rpg-xml.com/example/xsdvalidate/success.xml';
         TransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
         request = *Blanks;
         goodXml = RXS_Transmit( request : TransmitDS );

         // Get bad XML to test validation
         RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
         TransmitDS.Uri =
           'http://files.rpg-xml.com/example/xsdvalidate/failure.xml';
         TransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
         request = *Blanks;
         badXml = RXS_Transmit( request : TransmitDS );

         // Get XSD from remote URI
         RXS_ResetDS( TransmitDS : RXS_DS_TYPE_TRANSMIT );
         TransmitDS.Uri =
           'http://files.rpg-xml.com/example/xsdvalidate/validate.xsd';
         TransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
         request = *Blanks;
         xsd = RXS_Transmit( request : TransmitDS );

         // Put the XSD in an IFS file
         RXS_ResetDS( PutStmfDS : RXS_DS_TYPE_PUTSTMF );
         PutStmfDS.Stmf = '/tmp/rxs_xsd_validate.xsd';
         PutStmfDS.StmfCcsid = RXS_CCSID_ISO88591;
         RXS_PutStmf( xsd : PutStmfDS );

       endsr;

       begsr Cleanup;

         RXS_ResetDS( DeleteStmfDS : RXS_DS_TYPE_DELETESTMF );
         DeleteStmfDS.Stmf = PutStmfDS.Stmf;
         RXS_ProcessStmf( DeleteStmfDS );

       endsr;

      /END-FREE
```

---

# RESTXML

> RPG example consuming a REST XML web service via HTTP GET and parsing the response with the event-based XML parser.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to consume a REST based XML web service via HTTP GET, and parse the response XML with event-based parsing.

```rpgle
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')

      /copy QRPGLECPY,RXSCB                      RPG-XML Suite Prototypes & Definitions

     D XmlHandler      PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D gParseDS        DS                  LikeDS(RXS_ParseDS_t)
     D gTransmitDS     DS                  LikeDS(RXS_TransmitDS_t)
     D ErrorDS         DS                  LikeDS(RXS_CatchThrowErrorDS_t)

     D gXmlResponse    S                   Like(RXS_Var64Kv_t)
     D gNameList       S                   Like(RXS_Var64Kv_t)
     D gCount          S             10I 0

      /free

       // Please note that the REST web service being consumed by this example program
       //   is not 100% available due to metering being performed on the
       //   "demo" account. Usually you can make multiple attempts over time and
       //   get a proper response eventually.

       monitor;

         // Transmit

         RXS_ResetDS( gTransmitDS : RXS_DS_TYPE_TRANSMIT );
         gTransmitDS.URI = 'http://api.geonames.org/cities'
                         + '?north=44.1&south=-9.9&east=-22.4'
                         + '&west=55.2&lang=de&username=demo';
         gTransmitDS.LogFile = '/tmp/log.txt';
         gTransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
         gXmlResponse = RXS_Transmit( *Omit : gTransmitDS );

         // Parse

         gCount = 0;
         RXS_ResetDS( gParseDS : RXS_DS_TYPE_PARSE );
         gParseDS.GlobalHandler = %Paddr( XmlHandler );
         RXS_Parse( gXmlResponse : gParseDS );

         // Output Parse Results as a Thrown Message

         RXS_ResetDS( ErrorDS : RXS_DS_TYPE_CATCHTHROWERROR );
         ErrorDS.MessageId = 'RXS9897';
         ErrorDS.MessageData = gNameList;
         ErrorDS.ThrowToCaller = RXS_YES;
         RXS_Throw( ErrorDS );


       on-error;

         RXS_Joblog('Unexpected error occurred. See previous messages.');

       endmon;

       *inlr = *on;

      /end-free

     P XmlHandler      B
     D XmlHandler      PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

     D base            s            256a   Varying
     D chgMe           s            256a   Varying

      /free

       base = '/geonames/geoname';
       select;

         when pXPath = '/geonames/>';
           if gCount > 0;
             gNameList += 'No more names!';
           endif;

         when pXPath = base + '/toponymName>';
           gCount += 1;
           gNameList += %char(gCount) + ': ';

         when pXPath = base + '/toponymName/>';
           gNameList += ', ';

         when pXPath = base + '/toponymName/';
           gNameList += RXS_STR( pData : pDataLen );

         // If the service is unavailable to the usage metering mentioned earlier,
         //   there will be XML data available for parsing per the below

         when pXPath = '/geonames/status@message';
           gNameList = RXS_STR( pData : pDataLen );

         when pXPath = '/geonames/status@value';
           gNameList += ' (' + RXS_STR( pData : pDataLen ) + ')';

         // The below code is for unused paths, but is included for clarity

         when pXPath = base + '/name/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/lat/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/lng/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/geonameId/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/countryCode/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/countryName/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/fcl/';
           chgMe = RXS_STR( pData : pDataLen );

         when pXPath = base + '/fcode/';
           chgMe = RXS_STR( pData : pDataLen );

       endsl;

      /end-free

     P                 E
```

---

# RESTJSON

> RPG example consuming a REST JSON web service via HTTP GET and parsing the JSON response.

This example program demonstrates the basic structure of an RPG program utilizing RPG-XML Suite to consume a REST based JSON web service via HTTP GET, and parse the JSON response.

```rpgle
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')

      /copy QRPGLECPY,RXSCB                      RPG-XML Suite Prototypes & Definitions

     D JSONHandler     PR              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const

     D gTransmitDS     DS                  LikeDS(RXS_TransmitDS_t)
     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
     D ErrorDS         DS                  LikeDS(RXS_CatchThrowErrorDS_t)

     D Json            S                   Like(RXS_Var64Kv_t)

     D gNameList       S                   Like(RXS_Var64Kv_t)
     D gCount          S             10I 0

      /free

       // Please note that the REST web service being consumed by this example program
       //   is not 100% available due to metering being performed on the
       //   "demo" account. Usually you can make multiple attempts over time and
       //   get a proper response eventually.

       monitor;

         // Transmit

         RXS_ResetDS( gTransmitDS : RXS_DS_TYPE_TRANSMIT );
         gTransmitDS.URI = 'http://api.geonames.org/citiesJSON'
                         + '?north=44.1&south=-9.9&east=-22.4'
                         + '&west=55.2&lang=de&username=demo';
         gTransmitDS.LogFile = '/tmp/log.txt';
         gTransmitDS.HTTPMethod = RXS_HTTP_METHOD_GET;
         Json = RXS_Transmit( *Omit : gTransmitDS );

         // Parse

         gCount = 0;
         RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
         ParseJsonDS.Handler = %Paddr( JSONHandler );
         RXS_ParseJson( Json : ParseJsonDS );

         // Output Parse Results as a Thrown Message

         RXS_ResetDS( ErrorDS : RXS_DS_TYPE_CATCHTHROWERROR );
         ErrorDS.MessageId = 'RXS9897';
         ErrorDS.MessageData = gNameList;
         ErrorDS.ThrowToCaller = RXS_YES;
         RXS_Throw( ErrorDS );


       on-error;

         RXS_Joblog('Unexpected error occurred. See previous messages.');

       endmon;

       *inlr = *on;

      /end-free

     P JSONHandler     B                   Export
     D                 PI              N
     D  pType                         5I 0 Const
     D  pPath                              Const Like(RXS_Var64Kv_t)
     D  pIndex                       10U 0 Const
     D  pData                          *   Const
     D  pDataLen                     10U 0 Const

      /free

       select;

         when pType = RXS_JSON_OBJECT_END
          and pPath = '/geonames[*]';
           gNameList += ', ';

         when pType = RXS_JSON_OBJECT_END
          and pPath = '/';
           if gCount > 0;
             gNameList += 'No more names!';
           endif;

         when pType = RXS_JSON_STRING
          and pPath = '/geonames[*]/toponymName';
           gCount += 1;
           gNameList += %char(gCount) + ': ';
           gNameList += RXS_STR( pData : pDataLen );

         // If the service is unavailable to the usage metering mentioned earlier,
         //   there will be JSON data available for parsing per the below

         when pPath = '/status/message';
           gNameList = RXS_STR( pData : pDataLen );

         when pPath = '/status/value';
           gNameList += ' (' + RXS_STR( pData : pDataLen ) + ')';

       endsl;

       // If you want to stop parsing early for whatever reason, return
       // RXS_JSON_STOP_PARSING

       return RXS_JSON_CONTINUE_PARSING;

      /end-free

     P                 E
```

---

# ORDERSVC

> Complete web service example that parses an XML request into ORDSVCHDR/ORDSVCDTL tables, composes an XML response using the template engine, and configures RXS Router for access.

This example program demonstrates how to offer a web service that accepts XML request data, parses the request data into the QORDHDR and QORDDTL tables, then composes and returns XML response data.

The XML composition template file can be found in the second tab - this template is required in order to compose the response XML data. You will need to compile it using [CRTRPGTPL](https://isupport.katointegrations.com/rxs/3.3/examples/crtrpgtpl.md).

Because this example is offering a web service, it would need a corresponding [RXS Router entry](https://isupport.katointegrations.com/rxs/3.3/examples/wrkrxsrtre.md). Here is an example entry:

![Example RXS router entry for ordersvc](https://isupport.katointegrations.com/llms/rpg-api-express-3.3/rxsrtre_ordersvc.png)

Where ORD is the library in which the program resides. This library will be added to the top of the library list when the service is called; the RXS library will be added automatically. The URL to access this service with this configuration would be:

`[your IBM i address]/orders/ordersvc`

Please see this tutorial for more information: [Configuring RXS Router](https://isupport.katointegrations.com/rxs/3.3/examples/configuring_rxs_router.md).

### ORDERSVC.rpgle

```rpgle
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')

     FQORDHDR   IF A E           K DISK    UsrOpn Prefix('H')
     FQORDDTL   IF A E           K DISK    UsrOpn Prefix('D')

      /COPY QRPGLECPY,RXSCB
      * This is the first of two times where we need to copy in the template member
      *  for our response XML - it is copied a second time in the ResponseTemplate
      *  subprocedure
      /COPY QRPGLETPL,ORDERTPL

      * ========================================================================
      * Internal prototypes
      * ========================================================================
     D RequestHandler  PR
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value

      * ========================================================================
      * External prototypes
      * ========================================================================

      * ========================================================================
      * Global fields and data structures
      * ========================================================================
     D RequestXml      S                   Like(RXS_Var8Kv_t)
     D ResponseXml     S                   Like(RXS_Var64Kv_t)
     D ParseDS         DS                  LikeDS(RXS_ParseDS_t)
     D StdOutDS        DS                  LikeDS(RXS_PutStdOutDS_t)
     D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
     D gOrderNumber    S              5P 0
     D gInvoiceTotal   S             11P 2
     D gItemsCount     S              3P 0
      /FREE
       monitor;
         // Open necessary tables
         if not %Open( QORDHDR );
           open QORDHDR;
         endif;
         if not %Open( QORDDTL );
           open QORDDTL;
         endif;

         // Retrieve request data
         reset RequestXml;
         RequestXml = RXS_GetStdIn();

         // Parse request data and write records
         RXS_ResetDS( ParseDS : RXS_DS_TYPE_PARSE );

         ParseDS.GlobalHandler = %Paddr( RequestHandler );
         RXS_Parse( RequestXml : ParseDS );

         // Compose the response XML
         reset V;
         reset S;

         RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
         ComposeDS.TemplateProcedure = %Paddr( ResponseTemplate );
         RXS_StartComposeEngine( ComposeDS );

         RXS_ComposeVariable( V.OrderNumber : %Char(HORDER) );
         RXS_ComposeVariable( V.OrderDate : %Char(HORDDAT) );
         RXS_ComposeVariable( V.OrderStatus : %Char(HORDSTS) );
         RXS_ComposeVariable( V.CustNum : %Trim( HCUST ) );
         RXS_ComposeVariable( V.ShipVia : %Trim( HSHPVIA ) );
         RXS_ComposeSection( S.ORDER_HEADER );

         RXS_ComposeVariable( V.InvoiceNumber : %Char(HINVNUM) );
         RXS_ComposeVariable( V.ItemCount : %Char(HTOTLIN) );
         RXS_ComposeVariable( V.Amount : %Char(HORDAMT) );
         RXS_ComposeSection( S.INVOICE_HEADER );

         read ORDDTL;
         dow not %Eof();
           if DORDER = HORDER;
             RXS_ComposeVariable( V.ItemNumber : %Char(DPARTNO) );
             RXS_ComposeVariable( V.ItemDesc : %Trim(DPARTDESC) );
             RXS_ComposeVariable( V.ItemQuantity : %Char(DQTYORD) );
             RXS_ComposeVariable( V.UnitPrice : %Char(DUNITAMT) );
             RXS_ComposeVariable( V.TotalPrice : %Char(DTOTAMT) );
             RXS_ComposeSection( S.ITEM );
           endif;
           read ORDDTL;
         enddo;

         RXS_ComposeSection( S.INVOICE_FOOTER );

         RXS_ComposeSection( S.ORDER_FOOTER );

         ResponseXml = RXS_GetComposeBuffer();

         // Return the response XML
         RXS_ResetDS( StdOutDS : RXS_DS_TYPE_PUTSTDOUT );

         StdOutDS.HeaderContentType = 'text/xml';
         RXS_PutStdOut( ResponseXml : StdOutDS );
         RXS_JobLog( ResponseXml );

         // Close files
         if %Open( QORDHDR );
           close QORDHDR;
         endif;
         if %Open( QORDDTL );
           close QORDDTL;
         endif;
       on-error;
         exsr teardown;
       endmon;

       return;

       // ----------------------------------------------------------------------
       begsr teardown;
         if %Open( QORDHDR );
           close(E) QORDHDR;
         endif;
         if %Open( QORDDTL );
           close(E) QORDDTL;
         endif;
       endsr;
      /END-FREE

      * ========================================================================
      * Internal subprocedures
      * ========================================================================
      * ========================================================================
      * Request XML parsing handler
      * ========================================================================
      * This subprocedure handles XML events that are detected by the parser.
      * The XPath structures for the different events are:
      *   '/element>' - element start
      *   '/element/>' - element end
      *   '/element/' - element content
      *   '/element@attribute' - attribute content
     P RequestHandler  B
     D                 PI
     D  pType                        10A   Value
     D  pXPath                     1024A   Value Varying
     D  pData                          *   Value
     D  pDataLen                     10I 0 Value
      /FREE
       RXS_JobLog( pXPath );
       select;
         // This xpath ends in a ">", which means that is it an "Element Begin"
         //  parsing event. These types of events are generally a good trigger
         //  to perform intialization of records or data structures, or reset
         //  counters, etc.
         // Note that "Element Begin" events DO NOT have data.
         when pXPath = '/Order>';
           // At the start of the XML document, reset the QORDHDR record and
           //  zero the invoice total field
           reset ORDHDR;
           gInvoiceTotal = 0;
           gItemsCount = 0;

         when pXPath = '/Order/Items/Item>';
           reset ORDDTL;

         // This xpath ends in a "/>", which is an "Element End" parsing event.
         //  These events are a good place to perform record write or cleanup
         //  operations.
         // Note that "Element End" events DO NO have data.
         when pXPath = '/Order/>';
           HORDAMT = gInvoiceTotal;
           HTOTLIN = gItemsCount;
           HORDSTS = 1;
           HORDDAT = %Dec( %Char(%Date():*YMD0) : 6 : 0 );
           HINVNUM = HORDER + 1;
           write ORDHDR;

         // We will use this event to calculate the line item total cost
         //  and add it to the invoice total, as well as write the line item
         //  record to the table. We also incremement the item count.
         when pXPath = '/Order/Items/Item/>';
           DORDER = HORDER;
           DTOTAMT = DUNITAMT * DQTYORD;
           gInvoiceTotal += DTOTAMT;
           gItemsCount += 1;
           write ORDDTL;

         // This xpath is searching for an "Attribute Content" parsing event,
         //  which is indicated by the '@' symbol before the attribute name.
         // Note that "Attribute Content" events DO have data.
         when pXPath = '/Order@Number';
           HORDER = %Dec( RXS_STR( pData : pDataLen ) : 5 : 0 );

         // This xpath ends in a "/", which is an "Element Content" parsing event.
         //  These events are the main thing you test for when writing an event
         //  handler subprocedure, because this is how you parse content from an
         //  XML document.
         // Note that "Element Content" events DO have data.
         when pXPath = '/Order/Customer/';
           HCUST = RXS_STR( pData : pDataLen );

         when pXPath = '/Order/Ship/';
           HSHPVIA = RXS_STR( pData : pDataLen );
           // Parsed data is retrieved with RXS_STR().
           // Because content is retrieved as character data, we need to
           //  convert the value into the appropriate data type before
           //  writing it to the table.
         when pXPath = '/Order/Items/Item/Number/';
           DPARTNO = %Dec( RXS_STR( pData : pDataLen ) : 6 : 0 );

         when pXPath = '/Order/Items/Item/Description/';
           DPARTDESC = RXS_STR( pData : pDataLen );

         when pXPath = '/Order/Items/Item/Quantity/';
           DQTYORD = %Dec( RXS_STR( pData : pDataLen ) : 3 : 0 );

         when pXPath = '/Order/Items/Item/UnitPrice/';
           DUNITAMT = %Dec( RXS_STR( pData : pDataLen ) : 7 : 2 );

       endsl;

       return;
      /END-FREE
     P                 E

      * ========================================================================
      * Response XML composition template subprocedure
      * ========================================================================
     P ResponseTemplate...
     P                 B
     D                 PI
     D  p                                  Like(RXS_TEMPLATE_PARM)
        // Templates are created via a two-step process using BLDTPL and
        //  CRTRPGTPL - full walkthrough can be found at
        // https://isupport.katointegrations.com/rxs/latest/creating_an_rxs_template/

        // The RPG Template source is copied from QRPGLETPL by using
        //  /COPY once in the main program D-specs and again below.

      /COPY QRPGLETPL,ORDERTPL
     P                 E
```

### ORDERTPL.tpl

```rpgle
::ORDER_HEADER
<Order Number=".:OrderNumber:." Date=".:OrderDate:.">
  <Status>.:OrderStatus:.</Status>
  <Customer>.:CustNum:.</Customer>
  <Ship>.:ShipVia:.</Ship>
::INVOICE_HEADER
  <Invoice Number=".:InvoiceNumber:." ItemCount=".:ItemCount:.">
    <Amount>.:Amount:.</Amount>
    <Items>
::ITEM
      <Item>
        <ItemNumber>.:ItemNumber:.</ItemNumber>
        <Description>.:ItemDesc:.</Description>
        <Quantity>.:ItemQuantity:.</Quantity>
        <UnitPrice>.:UnitPrice:.</UnitPrice>
        <TotalPrice>.:TotalPrice:.</TotalPrice>
      </Item>
::INVOICE_FOOTER
    </Items>
  </Invoice>
::ORDER_FOOTER
</Order>
```
