Janus SOAP XmlDoc API V7.8 changes

From m204wiki
Jump to navigation Jump to search

{{#hierarchy-top:}}

The following sections describe changes in the Janus SOAP XmlDoc API in this release.

XPathError exceptions

All XmlDoc and XmlNode class methods that accept XPath arguments now can throw an XPathError exception.

An example of using the XPathError exception is:

%err Object XPathError Try %d:Print('a b c') Catch XPathError To %err PrintText {~} = {%err:Reason} PrintText {~} = {%err:Description} PrintText {~} = {%err:CharacterPosition} End Try

Since the expression in the above invocation of the Print method (a b c) is not a valid XPath expression, the above fragment will result in the following:

%err:Reason = SyntaxError %err:Description = Expect "/" for new step or "[" for predicate %err:CharacterPosition = 3

The methods in the XmlDoc and XmlNode classes that can throw an XPathError exception are:

  • Audit
  • DefaultURI
  • DeleteSubtree
  • Exists
  • Length
  • LocalName
  • Prefix
  • PrefixURI
  • Print
  • Qname
  • SelectCount
  • SelectNodes
  • SelectSingleNode
  • Serial
  • ToXpathStringlist
  • Trace
  • Type
  • UnionSelected
  • Uri
  • Value
  • ValueDefault
  • XPathNodeID

The XPathError exception class

The members of the XPathError exception class are described below. Except for the constructor, New, all class members are ReadOnly properties:

Reason An enumeration of type XmlPathErrorReason. The possible values are:
SyntaxError A violation of the syntax of an XPath expression.
EmptyResult There are no nodes matched by the XPath expression, and the method requires at least one matching node.
CharacterPosition The position within the XPath expression at or before which the error was detected.
Description A message that explains the error.
New The constructor for the class, New lets you set values for each member of the class.

%ex = New ( Reason = reasonEnum - [, CharacterPosition = num] - [, Description = string] )

The Reason argument is required. All other arguments are optional, NameRequired, and have default values of the null string or 0 as appropriate.

NewFromRecord shared function in XmlDoc class

This shared function creates a new XmlDoc object which contains the fields and fieldgroups from the current record. This record extraction is the same operation that is performed by the LoadFromRecord subroutine and by the ToXmlDoc function in the Record class.

Whether to use ToXmlDoc, NewFromRecord, or LoadFromRecord depends on what is most convenient for your application. If you are already using a Record object which references the desired record, using ToXmlDoc may be more convenient; if not, then either NewFromRecord or LoadFromRecord (both of which require that the method be contained in a “record loop”) may be more convenient. You must use LoadFromRecord if you want to add the record's content as a subtree to a non-empty XmlDoc; in other cases the NewFromRecord “factory method” may be your choice.

Since NewFromRecord and ToXmlDoc create new XmlDoc objects, they have the AllowNull argument for setting the created XmlDoc's AllowNull poperty; LoadFromRecord does not have the AllowNull argument.

As stated, both NewFromRecord and LoadFromRecord must be contained in a “record loop”, for example, an FRN block, and they may not be invoked within a fieldgroup context.

Except for these considerations, ToXmlDoc, NewFromRecord, and LoadFromRecord all perform the same operation and have the same arguments. The discussion of the “extract from record to XmlDoc” operation, generally uses LoadFromRecord, except where one of these considerations is relevant to the discussion.

The arguments to NewFromRecord are shown in this statement:

%newXmlDoc = %(XmlDoc):NewFromRecord( - [AttributeValues=bool] - [, AttributeNames=bool] - [, NamesToLower=bool] - [, AllowUnreversible=bool] - [, CodepageTable=bool] - [, AllowNull=bool] )

Descriptions of some of the arguments follow:

%newXmlDoc NewFromRecord returns a new XmlDoc object.
%(XmlDoc) The class name in parentheses denotes a shared method and is one way to invoke NewFromRecord. You can also use an object expression whose type is XmlDoc (even if the value of the expression is null).
AllowNull The value of this Boolean argument, which defaults to False, is copied to the AllowNull property of the XmlDoc created by NewFromRecord.

The XmlDoc's AllowNull property, in turn, determines whether field values that contain the X'00' character are stored in the XmlDoc with base64 encoding. Such values are base64 encoded if AllowNull is False.

A False value of the AllowNull property of an XmlDoc is its default. This prevents null characters from being stored in the XmlDoc, so it will be conformant to the XML Recommendation, which does not allow null characters in an XML document.

The following fragment:

%s = 'Field with null/' With '00':X With '/' Store Record FOO = %s End Store %r = $CurRec FRN %r %doc = %doc:NewFromRecord End For PrintText {~} = {%doc:AllowNull} %doc:Print

produces this output:

%doc:AllowNull = False <Record version="1" file="QAWORK" number="1"> <field name="FOO" encoding="base64"> xomFk4RApomjiECVpJOTYQBh </field> </Record>

In the above output, notice that FOO is base64 encoded, because it contains a null character, and null characters are not allowed in an XmlDoc whose AllowNull property is False. The following fragment:

%s = 'Field with null/' With '00':X With '/' Store Record FOO = %s End Store %r = $CurRec FRN %r %doc = %doc:NewFromRecord(AllowNull=True) End For PrintText {~} = {%doc:AllowNull} %doc:Print

produces the following output:

%doc:AllowNull = True <Record version="1" file="QAWORK" number="1"> <field name="FOO"> Field with null/&#x0;/ </field> </Record>

In the above output, FOO is not base64 encoded; the XmlDoc contains a null character, which is displayed by the Print method using a character reference (&#x0;). This may be useful for visually inspecting the contents of the XmlDoc, again noting that such a document is not, strictly speaking, conformant to the XML Recommendation.

See the description of the AttributeValue argument of the LoadFromRecord subroutine for a list of all conditions which force base64 encoding of the "field" element.
Other NewFromRecord arguments See LoadFromRecord for a discussion of all the other arguments of NewFromRecord.

See the LoadFromRecord section below for a discussion of extracting the contents of the current record into an XmlDoc.

Note that this method was actually introduced in version 7.6 of the Sirius Mods, although the AllowNull argument was not provided until version 7.7 of the Sirius Mods.

LoadFromRecord subroutine in XmlDoc and XmlNode classes

This subroutine adds to an XmlDoc object a subtree that contains the fields and fieldgroups from the current record. This record extraction is the same operation that is performed by the NewFromRecord function and by the ToXmlDoc function in the Record class.

Whether to use ToXmlDoc, NewFromRecord, or LoadFromRecord depends on what is most convenient for your application. If you are already using a Record object that references the desired record, using ToXmlDoc may be more convenient; if not, then either NewFromRecord or LoadFromRecord (both of which require that the method be contained in a “record loop”) may be more convenient. You must use LoadFromRecord if you want to add the record's content as a subtree to a non-empty XmlDoc; in other cases, the NewFromRecord “factory method” may be your choice.

Since NewFromRecord and ToXmlDoc create new XmlDoc objects, they have the AllowNull argument for setting the created XmlDoc's AllowNull property. LoadFromRecord does not have the AllowNull argument.

As stated, both NewFromRecord and LoadFromRecord must be contained in a “record loop” (for example, an FRN block), and they may not be invoked within a fieldgroup context.

Except for these considerations, ToXmlDoc, NewFromRecord, and LoadFromRecord all perform the same operation and have the same arguments. The discussion of the “extract from record to XmlDoc” operation generally uses LoadFromRecord, except where one of these considerations is relevant to the discussion.

The arguments to LoadFromRecord are shown in the following statement:

%xmlNr:LoadFromRecord( - [AttributeValues=bool] - [, AttributeNames=bool] - [, NamesToLower=bool] - [, AllowUnreversible=bool] - [, CodepageTable=bool] )

Where:

%xmlNr LoadFromRecord is in both the XmlDoc and XmlNode classes. When the method object is an XmlDoc, or refers to the Root node of an XmlDoc:
  • The XmlDoc must not contain any nodes except the Root node.
  • A 'Record' element is added as the top level element of the XmlDoc. Children are added to the 'Record' element, representing the outer fields and fieldgroups of the record.

When the method object is an XmlNode not referring to the Root node: of an XmlDoc:

  • The node must be an Element node.
  • Children are added to that element, representing the outer fields and fieldgroups of the record.
  • See the AddToRecord subroutine for some examples of extracting multiple records into a single XmlDoc.

See Structure of XmlDoc for AddToRecord for a description of the XmlDoc created by LoadFromRecord.

AttributeValues This name required argument is a Boolean value that indicates whether a field value will be stored as “XML text” or as an XML attribute (belonging to its field, which is an XmlDoc element).

For example, <APSUBUND>COMM</APSUBUND> is text format, and <APSUBUND value="COMM"/> is attribute value format.

The default value is False, which produces text format. In this format, the value of a field will be converted to base64 in the XmlDoc if the field contains a byte that is:

  • Equal to X'00', if the AllowNull property of the XmlDoc is False.
  • Between X'00' and X'3F'.
  • Not translatable from EBCDIC to Unicode, using either the standard Unicode translation table or the base codepage translation table, as determined by the CodepageTable= argument.
  • Not invertible when translating from EBCDIC to Unicode and back to EBCDIC using the standard Unicode translation table, if the CodepageTable argument is False.

This argument must be False if the XmlDoc is to be used as the method object of the AddToRecord subroutine.

AttributeNames This name required argument is a Boolean value that indicates whether each field name is to be stored in the XmlDoc as an element name or as the value of a 'name' attribute.

For example, <APSUBUND>COMM</APSUBUND> is element-name format, and the following is name-as-attribute format:

<field name="APSUBUND"> COMM </field>

The default value as of Sirius Mods version 7.6 (and maintenance back to version 7.3) is True, which produces name-as-attribute format. Formerly, the default value was False.

The name-as-attribute format from the True option is better suited to operations on the XmlDoc, particularly a record copying operation. The element-name format from the False option produces more compact output when the XmlDoc is serialized.

This argument must be True if the XmlDoc is to be used as the method object of the AddToRecord subroutine.

NamesToLower This name required argument is a Boolean value that indicates whether field names are stored in all lowercase characters. The default value is False, which does not translate uppercase name characters to lowercase.

If the XmlDoc is to be used for record copying, this argument should probably be False.

AllowUnreversible This name required argument is a Boolean value that indicates whether a request is cancelled if a field name would be changed irreversibly by lowercasing or by replacing with a period the characters that would be invalid in an XML document.

The default value is False, which allows request cancellation to alert you about unreversible field names.

If the XmlDoc is to be used for record copying, this argument should probably be False.

CodepageTable This name required argument is a Boolean value; if True, the translations defined by the base Unicode codepage are used when translating from EBCDIC to Unicode for storing in the XmlDoc.

This argument is for the unusual case where you anticipate that the XML document is to be used later by the AddToRecord subroutine, and the standard Unicode translation tables in place when AddToRecord is invoked may differ from those in place when the record was copied to the XmlDoc.

The default value is False, which uses the standard Unicode translation tables, including any modifications specified in UNICODE Trans or UNICODE Map commands. The advantage of using CodepageTable=False is that it will allow you to readily modify the XmlDoc directly (that is, with the AddElement and AddAttribute methods). Those operations will use the standard Unicode translation tables; there is no way to perform them using the base codepage translation tables.

All fields are copied to the XmlDoc by LoadFromRecord.

See the AddToRecord subroutine for an example of extracting a record to an XmlDoc for record copying, and see the ToXmlDoc function in the Record class for other examples using the method arguments.

This method was actually introduced in version 7.6 of the Sirius Mods, but the XmlNode class LoadFromRecord implementation prior to version 7.8 required that the XmlDoc be empty.

AddToRecord subroutine in XmlDoc class

This subroutine adds to the current record the fields and/or fieldgroups that are contained in the method XmlDoc object according to the structure created by the LoadFromRecord subroutine. AddToRecord must be contained within a “record loop” (for example, an FRN block), and must not be contained in a fieldgroup context.


The arguments to AddToRecord are:

    %doc:AddToRecord(                     -
         [DisableFieldConstraints=bool]    -
         [, CopyIDs=bool]                  -
         [, IgnoreUndefinedFields=bool] )

Where

%doc
The method object is an XmlDoc whose structure conforms to that created by the LoadFromRecord subroutine; see Structure of XmlDoc for AddToRecord.
DisableFieldConstraints= bool
This name required argument is a Boolean value; if True, then various field constraints (such as LENGTH-EQ, which was introduced in Model 204 V7R2) are not checked when the fields are added to the record. The default is False.
CopyIDs= bool
This name required argument is a Boolean value; if True, then the fieldgroup IDs are copied, if possible, from the XmlDoc (stored as the 'groupID' attribute of 'fieldgroup' elements) to the record, and the 'maxGroupID' attribute value of the 'Record' element is also used as the maximum fieldgroup ID in the record, if possible. On any given 'fieldgroup' element, a 'groupID' attribute may be 0 or missing or not greater than the current maximum fieldgroup ID; in any of these cases, a new fieldgroup ID is generated for the fieldgroup occurrence. If False, new fieldgroup IDs are generated. The default is False.
IgnoreUndefinedFields= bool
This name required argument is a Boolean value; if True, then:
  • If a field in the XmlDoc is not defined in the current file, the field is ignored.
  • If a fieldgroup in the XmlDoc is not defined in the current file, the fieldgroup, and all descendants of the fieldgroup element, are ignored.

If False, any field or fieldgroup in the XmlDoc that is not defined in the current file throws an AddToRecordError exception.

The default is False.

The AddToRecord subroutine can throw an AddToRecordError exception.

Fields in the XmlDoc that are defined as CAT or CTO are ignored by AddToRecord.

Any fieldgroup update tracking fields in a fieldgroup are automatically set when that fieldgroup is created by AddToRecord, but no other updated tracking fields are automatically set by the updates performed by AddToRecord.

Any fields in the XmlDoc that are defined as update tracking fields are copied to the record. See Removing update tracking fields from an XmlDoc for an example of removing update tracking fields from AddToRecord's XmlDoc; generally this will not be needed, but it may be useful in certain situations.

The basic approach to copying a record from one file to another is:

    In SRCFILE FRN %source
       %doc = %doc:NewFromRecord
    End For
    In TARGFILE Store Record
    End Store
    %targ = $CurRec
    In TARGFILE FRN %targ
       %doc:AddToRecord
    End For

Some additional examples of record copying are shown in Copying from multiple source records.

Note that this method was actually introduced in version 7.6 of the Sirius Mods, although until version 7.8 of the Sirius Mods an exception was thrown if the version of Model 204 in use was older than V7R2.

Copying from multiple source records

Since the LoadFromRecord subroutine can extract from a record into a subtree of an Element node in an XmlDoc, you can use it, together with AddToRecord, to combine multiple source records into one target record.

As a simple example, you can put the fields from two records “side by side” into a target record:

    FRN %x
       %doc = %doc:NewFromRecord
    End For
    %top = %doc:SelectSingleNode('*')
    FRN %y
       %top:LoadFromRecord
    End For
    Store Record
    End Store
    %rn = $CurRec
    FRN %rn
       %doc:AddToRecord
    End For

The XmlDoc that is input to AddToRecord would have a structure similar to the following:

    <Record ...>
       <field ...>
       ... first field from record %x
       </field>
       ...
       <field ...>
       ... first field from record %y
       </field>
       ...
    </Record>

Of course, you could also accomplish this particular objective by using two XmlDocs:

    FRN %x
       %doc1 = %doc1:NewFromRecord
    End For
    FRN %y
       %doc2 = %doc2:NewFromRecord
    End For
    Store Record
    End Store
    %targ = $CurRec
    FRN %targ
       %doc1:AddToRecord
       %doc2:AddToRecord
    End For

You can also use LoadFromRecord to modify the fieldgroup structure within a record (using V7R2 of Model 204). For example, you could take the “outer” fields of one record and move them to be fieldgroup members in the target record:

    In SRCFILE FRN %x
       %doc = %doc:NewFromRecord
    End For
    %fg = %doc:SelectSingleNode('*/fieldgroup[@name="GRP"]')
    In SRCFILE FRN %y
       %fg:LoadFromRecord
    End For
    In TARGFILE Store Record
    End Store
    %rn = $CurRec
    In TARGFILE FRN %rn
       %doc:AddToRecord
       PAI
    End For

The use of a separate source and target file above (which in general is a typical usage of the record copying methods) is more or less required here, because the fields in SRCFILE are defined as outer fields, but in TARGFILE they are defined as members of fieldgroup GRP (or they would need to be “FIELDGROUP *” fields). So, for example, definitions for SRCFILE might include:

    DEFINE FIELD FOO
    DEFINE FIELDGROUP GRP

and definitions for TARGFILE might include:

    DEFINE FIELDGROUP GRP
    DEFINE FIELD FOO WITH FIELDGROUP GRP

And the XmlDoc that is input to the above AddToRecord subroutine may look like:

    <Record ...>
       <fieldgroup name="GRP" ...>
          <field name="FOO">
             value of foo
          </field>
       </fieldgroup>
    </Record>

And the corresponding output of the above PAI would be:

    \GRP = 1
     FOO = value of foo
    /GRP = 1

Removing update tracking fields from an XmlDoc

In the simple situation, the record copying operation provided by LoadFromRecord and AddToRecord produces a copy of all of the record's fields, including update tracking fields (such as UPDATE-TIME fields). However, in some circumstances, you may not want some of the update tracking fields to be propagated by AddToRecord. This can readily be accomplished by using DeleteSubtree to remove the tracking fields you want to suppress (of course, DeleteSubtree could be used to remove other fields and/or fieldgroups, as well).

Consider an example in which AddToRecord is being used to add fields to a record that already has fields stored in it:

    IN ?&SOURCE DEFINE FIELD REC.UPD WITH UPDATE-TIME
    IN ?&SOURCE DEFINE FIELD FOO
    IN ?&TARGET DEFINE FIELD REC.UPD WITH UPDATE-TIME
    IN ?&TARGET DEFINE FIELD FOO
     ...
    Do the following on Monday:
       In ?&SOURCE Store Record
          FOO = 'Record stored on Monday'
       End Store
       %rn = $CurRec
       In ?&SOURCE FRN %rn
          %doc = %doc:NewFromRecord
       End For
       %serial = %doc:Serial
       In LOG Store Record
          RECORD.XML = %serial
       End Store
     ...
    Do the following on Tuesday:
       In LOG For 1 Record
          %doc:LoadXml(RECORD.XML)
       End For
       In ?&TARGET Store Record
          FOO = 'Record stored on Tuesday'
       End Store
       %rn = $CurRec
       In ?&TARGET FRN %rn
          %doc:AddToRecord
       End For

In this scenario, GRP.UPD would be set to Monday, overlaying the more recent (and probably desired) value which was set on Tuesday. To prevent this overlay, you can insert the following statement after %doc:LoadXml(RECORD.XML):

    %doc:DeleteSubtree('*/fieldgroup/field[@name="GRP.UPD"]')

Structure of XmlDoc for AddToRecord

The method object of the AddToRecord subroutine is an XmlDoc whose structure conforms to that created by the LoadFromRecord subroutine. You can also create an XmlDoc to be used for AddToRecord; the rules for the XmlDoc are described here. The outline of the XmlDoc is:

    <Record [version="1"] [codepage="--codepage name--"]
       [maxGroupID="--fieldgrouop counter--"]
       [file="---file name---"] [number="---record number---"]>
       <field name="---field name---">
          ---field value---
       </field>
       <field name="---field name---" encoding="base64">
          ---base64 encoded field value---
       </field>
       <fieldgroup name="---fieldgroup name---"
          [groupID="---fieldgroup ID---"]>
           <field ...
           <fieldgroup ...
           ...
        </fieldgroup>
        ...
     </Record>

The requirements for the XmlDoc are:

Comments and PIs
Comment and PI nodes are allowed anywhere in the XmlDoc.
Elements in non-null namespaces
In addition to the elements mentioned below for the various elements contained in the XmlDoc, the 'Record' and 'fieldgroup' elements may have any additional child elements which are in any non-null namespace. These additional elements are ignored. For example, the following is a valid AddToRecord XmlDoc representing an empty record:
    <Record>
       <foo:bar xmlns:foo="u:xyz"/>
    </Record>

However, the only null-namespace child elements permitted of elements in the null namespace are those described for each element type below. For example, the following is an invalid AddToRecord XmlDoc:

    <Record>
       <foo/>
    </Record>
Attributes in non-null namespaces
In addition to the attributes mentioned below for the various elements contained in the XmlDoc, those elements may contain any additional attributes which are in any non-null namespace. These additional attributes are ignored. For example, the following is a valid AddToRecord XmlDoc representing an empty record:
    <Record foo:bar="x" xmlns:foo="u:xyz"/>

Of course, elements in non-null namespaces may have any attributes. The only attributes permitted in elements in the null namespace are those described below. For example, the following is an invalid AddToRecord XmlDoc:

    <Record foo="x"/>
Record element
The top level element of the XmlDoc must be named 'Record'; all of its attributes are optional and are described below. The element children of the 'Record' element are optional; they may be:
  • field
  • fieldgroup

The 'Record' element may not have a Text child node.

version attribute of Record element
If present, this attribute must have the numeric value '1'.
codepage
If present, this attribute contains the name of the codepage whose base translation tables are used for converting the Unicode field names and field values (if not base 64 encoded) in the XmlDoc to EBCDIC.
maxGroupID attribute of Record element
If present, this attribute contains the value of the maximum fieldgroup ID to be set (if greater than or equal to all of the fieldgroup IDs stored in the record) in the added record, if the CopyIDs=True argument is provided. This must be a non-negative integer value.
file attribute of Record element
This attribute is ignored.
number attribute of Record element
If present, this must either be "-1", signifying that the input record number is not known, or must be a non-negative integer value. As noted in AddToRecord constraint on 'number' attribute, this represents a compatibility issue.
field element
The 'field' element may be a child of either the 'Record' or 'fieldgroup' element; it contains a field occurrence. It must have a 'name' attribute, and may have an 'encoding' attribute. It may have one Text child, which is either the value of the occurrence or, if the 'encoding' attribute is present (with the value 'base64'), is the base 64 encoded value of the occurrence.
name attribute of field element
This attribute is required; it is the name of the field.
encoding attribute of field element
This attribute is optional; if present, it must have the value 'base64', indicating that the Text child of the 'field' element is the base 64 encoding of the field value.
fieldgroup element
The 'fieldgroup' element may be a child of either the 'Record' or 'fieldgroup' element; it is the top of a subtree representing a fieldgroup occurrence. The element children of the 'fieldgroup' element are optional; they may be:
  • field
  • fieldgroup

The fieldgroup element may not have a Text child node. It must have a 'name' attribute, and may have a 'groupID' attribute.

name attribute of fieldgroup element
This attribute is required; it is the name of the fieldgroup.
groupID attribute of fieldgroup element
If present, this attribute contains the value of the fieldgroup ID to be set (if possible) for the fieldgroup occurrence in the added record, if the CopyIDs=True argument is provided.

AddToRecordError exception class

The members of the AddToRecordError exception class are described below. Except for the constructor, New, all class members are read-only properties:

Reason
An enumeration of type AddToRecordErrorReason. The possible values are:
InvalidNode
A node in the XmlDoc does not conform to the structure as created by the LoadFromRecord subroutine.
UntranslatableFieldName
A field name in the XmlDoc is not translatable to EBCDIC.
UntranslatableFieldgroupName
A fieldgroup name in the XmlDoc is not translatable to EBCDIC.
UntranslatableValue
A field value in the XmlDoc is not translatable to EBCDIC.
InvalidBase64
A string used for the base64 encoding of a field in the XmlDoc is not a valid base64 string.
FieldNameTooLong
A field name in the XmlDoc is longer than 255 characters.
FieldgroupNameTooLong
A fieldgroup name in the XmlDoc is longer than 255 characters.
ValueTooLong
The value of a field in the XmlDoc that is not defined as a LOB field in the current file is longer than 255 characters or is longer than the defined LEN attribute, if the field is a fixed OCCURS field.
UnknownFieldName
A field name in the XmlDoc is not defined in the current file.
UnknownFieldgroupName
A fieldgroup name in the XmlDoc is not defined in the current file.
ExpectedField
A field name in the XmlDoc is defined as a fieldgroup in the current file.
ExpectedFieldgroup
A fieldgroup name in the XmlDoc is defined as a field in the current file.
ErrorAddingField
An error occurred adding a field, such as a violation of a field constraint.
ErrorAddingFieldgroup
An error occurred adding a fieldgroup, such as a file full condition.
ErrorObtainingRecord
AddToRecord was unable to lock the record in exclusive mode.
InvalidFieldgroupID
A fieldgroup ID in the XmlDoc is not numeric.
InvalidCodepage
The codepage name specified on the 'codepage' attribute of the 'Record' element is not a known codepage name.
ErrorAddingMaxFieldgroupID
The attempt to set the fieldgroup ID counter in the record failed; this is a very unusual condition.
InsufficientStorageForLOB
STBL, VTBL, or User Buffer storage was unavailable. The Description property indicates which of these is applicable.
InvalidVersion
Invalid value of the 'version' attribute of the 'Record' element; the only allowed value is 1.
InvalidInputRecordNumber
Invalid value of the 'number' attribute of the 'Record' element; it must either be "-1" or a non-negative integer.
Description
A message that explains the error.
UntranslatableHexValue
If the Reason indicates that a string in the XmlDoc is not translatable to EBCDIC, this contains the hexadecimal representation of the Unicode codepoint which is not translatable.
FieldOrFieldgroupName
If the error involves a field or fieldgroup (for example, if Reason = ErrorAddingField), this is the field or fieldgroup name.
NodeName
If the error involves a named node in the XmlDoc (for example, some cases when Reason = InvalidNode), this is the name of the node.
NodeType
If the error involves a node in the XmlDoc (for example, all cases when Reason = InvalidNode), this an XmlNodeType enumeration value of the type of the node.
Value
If the error involves a value in the XmlDoc (for example, when Reason = InvalidBase64), this is the value that is in error (actually, up to 255 bytes of the value).
InputRecordNumber
The value of the 'number' attribute of the 'Record' element in the XmlDoc.
New
The constructor for the class, New lets you set values for each member of the class.
    %ex = New ( Reason = reasonEnum                  -
              [, Description = string]               -
              [, UntranslatableHexValue = hexString] -
              [, FieldOrFieldgroupName = string]     -
              [, NodeName = string]                  -
              [, NodeType = xmlNodeTypeEnum]         -
              [, Value = string]                     -
              [, InputRecordNumber = number])

The Reason argument is required; all other arguments are optional, name required. The default value of InputRecordNumber is -1; all other default values are the null string or the Null object, as appropriate.

Note that this class was actually introduced in version 7.6 of the Sirius Mods.

MoveNamespace argument for AddTopElement

The following optional, name required argument has been added to the AddTopElement function in the XmlDoc class:

    MoveNamespace=boolean

If MoveNamespace=True and a URI argument (with a non-null string) is provided, then if the "old" top element contains a namespace declaration which is the same as the declaration inserted due to the URI argument, the declaration is deleted from the old top element.

Note: You should use MoveNamespace=True only when some consumer of the XML document cares about not having the redundant declaration. This would be an unusual situation, because the information content of the document does not change. Using MoveNamespace=True might trigger a scan of the entire XmlDoc, which AddTopElement does not normally do.

DeleteTopElement subroutine in XmlDoc class

The syntax for DeleteTopElement is:

    doc:DeleteTopElement

This subroutine removes the top Element from an XmlDoc, and make its children be the “middle siblings” of the left and right siblings of the deleted Element.

  • The deleted element may not have more than one Element child.
  • The deleted element may not have a Text child.

In common usage, the Element child of the deleted Element will become the new top Element of the XmlDoc. DeleteTopElement is a convenient, better-performing alternative to using AddSubtree to copy all but the top Element (or in multiple iterations, removing successive top Elements) from an XmlDoc.

For example, you can use it to remove the SOAP wrappers from an XmlDoc; if the contents of %xdoc are:

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
       <soap:Body>
          <data>
             ...
          </data>
       </soap:Body>
    </soap:Envelope>

Then the following User Language fragment:

    %xdoc:DeleteTopElement
    %xdoc:DeleteTopElement

Results in the following contents of %xdoc:

    <data>
       ...
    </data>

The remaining considerations for using DeleteTopElement concern the occurrence of namespace declarations in the XmlDoc; there are a few details to explain how they are manipulated, and to explain how the performance of DeleteTopElement is affected.

As shown in the “SOAP wrapper removal” example above, DeleteTopElement will remove from the XmlDoc any namespace declarations (in this example, xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/") which are not referenced after removing the top Element. Note that a variant on the above example can occur using a default namespace declaration:

    <Envelope xmlns"http://schemas.xmlsoap.org/soap/envelope/">
       <Body>
          <data  xmlns="">
             ...
          </data>
       </Body>
    </Envelope>

Then the following User Language fragment:

    %xdoc:DeleteTopElement
    %xdoc:DeleteTopElement

Results in the following contents of %xdoc:

    <data  xmlns="">
       ...
    </data>

As seen in this example, the null namespace declaration is not removed, even though it is not, strictly speaking, needed.

In addition, DeleteTopElement will move, to the new top Element, any namespace declarations which do continue to be referenced after the top Element deletion:

    %n = %d:AddElement('foo', , 'u:uri')
    %n:AddElement('bar', , 'u:uri')
    %d:Print
    %d:DeleteTopElement
    Print 'After deletion:'
    %d:Print

The result of the above User Language fragment is:

    <foo xmlns="u:uri">
       <bar/>
    </foo>
    After deletion:
    <bar xmlns="u:uri"/>

Regarding the performance of DeleteTopElement, it should always be significantly better than an approach using AddSubtree, and in many cases, it is constant, regardless of the size of the XmlDoc. For a detailed explanation of when the cost of DeleteTopElement might vary with the size of the XmlDoc, there are three reasons that the descendants of the new top Element need to be recursively visited:

  1. Fixing references to moved namespace declaration As mentioned above, one or more namespace declarations can be moved from the deleted element to the new top element. Having done that, any references to the moved namespace must be fixed up. A count is kept to the total references to moved namespace declarations; when this goes to zero there is no more need to do this. So, for example:
        <p:oldTop xmlns:p="p:uri">
           <p:newTop>
              <child/>
           </p:newTop>
        </p:oldTop>
    

    The p:newTop element needs to have it's namespace reference fixed, but that is the only reference to it, so there is no need to visit child.

  2. Fixing pointers to a deleted namespace declaration When a namespace declaration is deleted, it may be pointed to by other namespace declarations (there is a graph of them in the XmlDoc); these pointers need to be fixed up to point to the first non-deleted declaration following the deleted one. This is not needed by the descendants of any element which has a namespace declaration (in the original XmlDoc). So, for example:
        <p:oldTop xmlns:p="p:uri">
           <newTop>
              <q:child xmlns:q="q:uri">
                 <grandchild/>
              </q:child>
           </newTop>
        </p:oldTop>
    

    Both newTop and q:child need to have their namespace pointers fixed up, but since q:child has a namespace declaration, grandchild doesn't need to be fixed up, so it doesn't need to be visited.

  3. Turning off “ancestor may have non-null default namespace declaration” In order to make certain namespace operations faster, there is a "state" maintained at XmlDoc nodes, indicating that a non-null default namespace declaration may occur on an ancestor. This state is fixed up if DeleteTopElement deletes a non-null namespace declaration (as happens in the second “SOAP wrapper removal” example above, the one using a default namespace). The need to set this state is eliminated in descendants of an element which has a default namespace declaration (either null or not). So, for example:
        <oldTop xmlns="p:uri">
           <newTop>
              <q:child xmlns="q:uri">
                 <grandchild/>
              </q:child>
           </newTop>
        </p:oldTop>
    

    The state needs to be fixed at newTop, but since q:child has a default namespace declaration, the state at grandchild doesn't need to be fixed up, so it doesn't need to be visited.

{{#hierarchy-bottom:}}