Janus SOAP XmlDoc API V7.8 changes: Difference between revisions

From m204wiki
Jump to navigation Jump to search
m (Replaced content with "Content moved to M204Int, but history preserved here.")
 
(8 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Content moved to M204Int, but history preserved here.
The following sections describe changes in the <var class="product">[[Janus SOAP]]</var> [[XmlDoc API]] in this release.
==XPathError exceptions==
All <var>XmlDoc</var> and <var>XmlNode</var> class methods that accept XPath arguments
now can throw an [[#The XPathError exception class|XPathError exception]].
An example of using the <var>XPathError</var> exception is:
<p class="code"><nowiki>%err Object XPathError
Try
  %d:Print('a b c')
Catch XPathError To %err
  PrintText {~} = {%err:Reason}
  PrintText {~} = {%err:Description}
  PrintText {~} = {%err:CharacterPosition}
End Try
</nowiki></p>
Since the expression in the above invocation of the <var>Print</var> method
(<code>a b c</code>) is not a valid <var>[[XPath#XPath_syntax|Xpath expression]]</var>, the above fragment
will result in the following:
<p class="code"><nowiki>%err:Reason = SyntaxError
%err:Description = Expect "/" for new step or "[" for predicate
%err:CharacterPosition = 3
</nowiki></p>
The methods in the <var>XmlDoc</var> and <var>XmlNode</var> classes that can throw an
<var>XPathError</var> exception are:
<ul>
<li><var>Audit</var>
<li><var>DefaultURI</var>
<li><var>DeleteSubtree</var>
<li><var>Exists</var>
<li><var>Length</var>
<li><var>LocalName</var>
<li><var>Prefix</var>
<li><var>PrefixURI</var>
<li><var>Print</var>
<li><var>Qname</var>
<li><var>SelectCount</var>
<li><var>SelectNodes</var>
<li><var>SelectSingleNode</var>
<li><var>Serial</var>
<li><var>ToXpathStringlist</var>
<li><var>Trace</var>
<li><var>Type</var>
<li><var>UnionSelected</var>
<li><var>Uri</var>
<li><var>Value</var>
<li><var>ValueDefault</var>
<li><var>XPathNodeID</var>
</ul>
===The XPathError exception class===
The members of the <var>XPathError</var> exception class are described below.
Except for the constructor, <var>New</var>, all class members are
<var>[[Methods#Method definition syntax|ReadOnly]]</var> properties:
<table>
<tr><th>Reason
</th><td>An enumeration of type <var>XmlPathErrorReason</var>. The possible values are: <table> <tr><th>SyntaxError </th><td>A violation of the syntax of an <var>[[XPath#XPath_syntax|Xpath expression]]</var>. </td></tr> <tr><th>EmptyResult </th><td>There are no nodes matched by the XPath expression, and the method requires at least one matching node. </td></tr></table>
</td></tr>
<tr><th>CharacterPosition
</th><td>The position within the XPath expression at or before which the error was detected.
</td></tr>
<tr><th>Description
</th><td>A message that explains the error.
</td></tr>
<tr><th>New
</th><td>The constructor for the class, <var>New</var> lets you set values for each member of the class.
<p class="code"><nowiki>%ex = New ( Reason = reasonEnum        -
            [,&nbsp;CharacterPosition = num] -
            [,&nbsp;Description = string] )
</nowiki></p>
The <var>Reason</var> argument is required. All other arguments are optional,
[[Methods#Named parameters|NameRequired]], and have default values of the null string or 0 as appropriate.
</td></tr></table>
 
==NewFromRecord shared function in XmlDoc class==
This shared function creates a new <var>XmlDoc</var> 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 in XmlDoc and XmlNode classes|LoadFromRecord subroutine]] and by the [[ToXmlDoc (Record function)|ToXmlDoc function]] in the [[Record class]].
Whether to use <var>ToXmlDoc</var>, <var>NewFromRecord</var>, or <var>LoadFromRecord</var> depends on what is
most convenient for your application.
If you are already using a <var>Record</var> object which references the desired record,
using <var>ToXmlDoc</var> may be more convenient; if not, then either
<var>NewFromRecord</var> or <var>LoadFromRecord</var> (both of which require that the method be
contained in a &ldquo;record loop&rdquo;) may be more convenient.
You must use <var>LoadFromRecord</var> if you want to add the record's content as a
subtree to a non-empty <var>XmlDoc</var>;
in other cases the <var>NewFromRecord</var> &ldquo;factory method&rdquo; may be your choice.
Since <var>NewFromRecord</var> and <var>ToXmlDoc</var> create new <var>XmlDoc</var> objects, they have the
<var>AllowNull</var> argument for setting the created <var>XmlDoc</var>'s <var>AllowNull</var>
poperty; <var>LoadFromRecord</var> does not have the <var>AllowNull</var> argument.
As stated, both
<var>NewFromRecord</var> and <var>LoadFromRecord</var> must be
contained in a &ldquo;record loop&rdquo;, for example, an <var>FRN</var> block, and they may not be
invoked within a fieldgroup context.
Except for these considerations, <var>ToXmlDoc</var>, <var>NewFromRecord</var>, and <var>LoadFromRecord</var>
all perform the same operation and have the same arguments.
The discussion of the &ldquo;extract from record to <var>XmlDoc</var>&rdquo; operation,
generally uses <var>LoadFromRecord</var>, except where one of these considerations
is relevant to the discussion.
The arguments to <var>NewFromRecord</var> are shown in this syntax:
<p class="code"><nowiki>%newXmlDoc = %(XmlDoc):NewFromRecord(  -
            [AttributeValues=bool]      -
            [,&nbsp;AttributeNames=bool]    -
            [,&nbsp;NamesToLower=bool]      -
            [,&nbsp;AllowUnreversible=bool]  -
            [,&nbsp;CodepageTable=bool]      -
            [,&nbsp;AllowNull=bool] )
</nowiki></p>
Descriptions of '''''some of the arguments''''' follow:
<table>
<tr><th>%newXmlDoc</th>
<td><var>NewFromRecord</var> returns a new <var>XmlDoc</var> object.</td></tr>
<tr><th>%(XmlDoc)</th>
<td>The class name in parentheses denotes a shared method and is one way to invoke <var>NewFromRecord</var>. You can also use an object expression whose type is <var>XmlDoc</var> (even if the value of the expression is null).</td></tr>
<tr><th>AllowNull</th>
<td>The value of this [[Boolean]] argument, which defaults to <var>False</var>, is copied to the <var>AllowNull</var> property of the <var>XmlDoc</var> created by <var>NewFromRecord</var>.
The <var>XmlDoc</var>'s <var>AllowNull</var> property, in turn, determines whether
field values that contain the X'00' character are stored in the
<var>XmlDoc</var> with base64 encoding.
Such values are base64 encoded if <var>AllowNull</var> is <var>False</var>.
A <var>False</var> value of the <var>AllowNull</var> property of an <var>XmlDoc</var> is its default.
This prevents null characters from being stored in the <var>XmlDoc</var>, so
it will be conformant to the XML Recommendation, which does
not allow null characters in an XML document.
The following fragment:
<p class="code"><nowiki>%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
</nowiki></p>
produces this output:
<p class="output"><nowiki>%doc:AllowNull = False
<Record version="1" file="QAWORK" number="1">
  <field name="FOO" encoding="base64">
      xomFk4RApomjiECVpJOTYQBh
  </field>
</Record>
</nowiki></p>
In the above output, notice that <code>FOO</code> is base64 encoded,
because it contains a null character, and
null characters are not allowed in an <var>XmlDoc</var> whose <var>AllowNull</var>
property is <var>False</var>.
The following fragment:
<p class="code"><nowiki>%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
</nowiki></p>
produces the following output:
<p class="output"><nowiki>%doc:AllowNull = True
<Record version="1" file="QAWORK" number="1">
  <field name="FOO">
      Field with null/&amp;#x0;/
  </field>
</Record>
</nowiki></p>
In the above output, <code>FOO</code> is not base64 encoded; the <var>XmlDoc</var> contains
a null character, which is displayed by the <var>Print</var> method using a
character reference (<code>&amp;#x0;</code>).
This may be useful for visually inspecting the contents of the
<var>XmlDoc</var>, again noting that such a document is not, strictly speaking,
conformant to the XML Recommendation.
See the description of the <var>AttributeValue</var> argument of the [[#LoadFromRecord subroutine in XmlDoc and XmlNode classes|LoadFromRecord subroutine]] for a list of all conditions which force base64 encoding of the "field" element.</td></tr>
<tr><th>Other NewFromRecord  arguments</th>
<td>See [[#LoadFromRecord subroutine in XmlDoc and XmlNode classes|LoadFromRecord]] for a discussion of all the other arguments of <var>NewFromRecord</var>.</td></tr>
</table>
See the <var>[[#LoadFromRecord subroutine in XmlDoc and XmlNode classes|LoadFromRecord]]</var> section below for a discussion of extracting the contents of the current record into an <var>XmlDoc</var>.
Note that this method was actually introduced in version 7.6 of
the <var class="product">Sirius Mods</var>, although the <var>AllowNull</var> argument was
not provided until version 7.7 of the <var class="product">Sirius Mods</var>.
==LoadFromRecord subroutine in XmlDoc and XmlNode classes==
This subroutine adds to an <var>XmlDoc</var> 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 shared function in XmlDoc class|NewFromRecord
function]] and by the <var>[[ToXmlDoc (Record function)|ToXmlDoc]]</var> function in the <var>[[Record class|Record]]</var> class.
Whether to use <var>ToXmlDoc</var>, <var>NewFromRecord</var>, or <var>LoadFromRecord</var> depends on what is
most convenient for your application.
If you are already using a <var>Record</var> object that references the desired record,
using <var>ToXmlDoc</var> may be more convenient; if not, then either
<var>NewFromRecord</var> or <var>LoadFromRecord</var> (both of which require that the method be
contained in a &ldquo;record loop&rdquo;) may be more convenient.
You must use <var>LoadFromRecord</var> if you want to add the record's content as a
subtree to a non-empty <var>XmlDoc</var>;
in other cases, the <var>NewFromRecord</var> &ldquo;factory method&rdquo; may be your choice.
Since <var>NewFromRecord</var> and <var>ToXmlDoc</var> create new <var>XmlDoc</var> objects, they have the
<var>AllowNull</var> argument for setting the created <var>XmlDoc</var>'s <var>[[allowNull (XmlDoc property)|AllowNull]]</var>
property. <var>LoadFromRecord</var> does not have the <var>AllowNull</var> argument.
As stated, both
<var>NewFromRecord</var> and <var>LoadFromRecord</var> must be
contained in a &ldquo;record loop&rdquo; (for example, an <var>FRN</var> block), and they may not be
invoked within a fieldgroup context.
Except for these considerations, <var>ToXmlDoc</var>, <var>NewFromRecord</var>, and <var>LoadFromRecord</var>
all perform the same operation and have the same arguments.
The discussion of the &ldquo;extract from record to <var>XmlDoc</var>&rdquo; operation
generally uses <var>LoadFromRecord</var>, except where one of these considerations
is relevant to the discussion.
The arguments to <var>LoadFromRecord</var> are shown in the following syntax:
<p class="code"><nowiki>%xmlNr:LoadFromRecord(          -
      [AttributeValues=bool]    -
      [,&nbsp;AttributeNames=bool]    -
      [,&nbsp;NamesToLower=bool]      -
      [,&nbsp;AllowUnreversible=bool] -
      [,&nbsp;CodepageTable=bool] )
</nowiki></p>
Where:
<table>
<tr><th>%xmlNr
</th><td><var>LoadFromRecord</var> is in both the <var>XmlDoc</var> and <var>XmlNode</var> classes.
When the method object is an <var>XmlDoc</var>, or refers to the <var>Root</var> node of an <var>XmlDoc</var>: <ul> <li>The <var>XmlDoc</var> must not contain any nodes except the <var>Root</var> node. <li>A "Record" element is added as the top level element of the <var>XmlDoc</var>. Children are added to the Record element, representing the outer fields and fieldgroups of the record. </ul>
When the method object is an <var>XmlNode</var> not referring to the <var>Root</var> node of an <var>XmlDoc</var>:
<ul>
<li>The node must be an <var>Element</var> node.
<li>Children are added to that element, representing the outer fields and fieldgroups of the record.
<li>See the [[#AddToRecord subroutine in XmlDoc class|AddToRecord subroutine description]] for some examples of extracting multiple records into a single <var>XmlDoc</var>.
</ul>
See [[#Structure of XmlDoc for AddToRecord|"Structure of XmlDoc for AddToRecord"]] for a description of the <var>XmlDoc</var> created by <var>LoadFromRecord</var>.
</td></tr>
<tr><th>AttributeValues
</th><td>This [[Methods#Named parameters|name required]] argument is a <var>[[Boolean]]</var> value that indicates whether a field value will be stored as &ldquo;XML text&rdquo; or as an XML attribute (belonging to its field, which is an <var>XmlDoc</var> element).
For example, <code><APSUBUND>COMM</APSUBUND></code> is text format, and <code><APSUBUND value="COMM"/></code> is attribute value format.
The default value is <var>False</var>, which produces text format. In this format, the value of a field will be converted to base64 in the <var>XmlDoc</var> if the field contains a byte that is:
<ul>
<li>Equal to X'00', if the <var>[[AllowNull (XmlDoc property)|AllowNull]]</var> property of the <var>XmlDoc</var>
is <var>False</var>.
<li>Between X'00' and X'3F'.
<li>Not translatable from EBCDIC to Unicode, using either the standard Unicode translation table or the base codepage translation table, as determined by the <var>CodepageTable</var> argument.
<li>Not invertible when translating from EBCDIC to Unicode and back to EBCDIC using the standard Unicode translation table, if the <var>CodepageTable</var> argument is <var>False</var>.
</ul>
This argument must be <var>False</var> if the <var>XmlDoc</var> is to be used as the method object of the [[#AddToRecord subroutine in XmlDoc class|AddToRecord subroutine]].
</td></tr>
<tr><th>AttributeNames
</th><td>This [[Methods#Named parameters|name required]] argument is a <var>[[Boolean]]</var> value that indicates whether each field name is to be stored in the <var>XmlDoc</var> as an element name or as the value of a "name" attribute.
For example, <code><APSUBUND>COMM</APSUBUND></code> is element-name format, and the following is name-as-attribute format:
<p class="code"><nowiki><field name="APSUBUND">
  COMM
</field>
</nowiki></p>
The default value as of <var class="product">Sirius Mods</var> version 7.6 (and maintenance back to version 7.3) is <var>True</var>, which produces name-as-attribute format. Formerly, the default value was <var>False</var>.
The name-as-attribute format from the <var>True</var> option is better suited to operations on the <var>XmlDoc</var>, particularly a record copying operation. The element-name format from the <var>False</var> option produces more compact output when the <var>XmlDoc</var> is serialized.
This argument must be <var>True</var> if the <var>XmlDoc</var> is to be used as the method object of the <var>AddToRecord</var> subroutine.
</td></tr>
<tr><th>NamesToLower
</th><td>This name required argument is a <var>Boolean</var> value that indicates whether field names are stored in all lowercase characters. The default value is <var>False</var>, which does not translate uppercase name characters to lowercase.
If the <var>XmlDoc</var> is to be used for record copying, this argument should probably be <var>False</var>.
</td></tr>
<tr><th>AllowUnreversible
</th><td>This name required argument is a <var>Boolean</var> 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 <var>False</var>, which allows request cancellation to alert you about unreversible field names.
If the <var>XmlDoc</var> is to be used for record copying, this argument should probably be <var>False</var>.
</td></tr>
<tr><th>CodepageTable
</th><td>This name required argument is a <var>Boolean</var> value; if <var>True</var>, the translations defined by the '''base''' Unicode codepage are used when translating from EBCDIC to Unicode for storing in the <var>XmlDoc</var>.
This argument is for the unusual case where you anticipate that the XML document is to be used later by <var>AddToRecord</var>, and the standard Unicode translation tables in place when <var>AddToRecord</var> is invoked may differ from those in place when the record was copied to the <var>XmlDoc</var>.
The default value is <var>False</var>, which uses the standard Unicode translation tables, including any modifications specified in <code>UNICODE Trans</code> or <code>UNICODE Map</code> commands.
The advantage of using <code>CodepageTable=False</code> is that it will allow you to readily modify the <var>XmlDoc</var> directly (that is, with the <var>[[AddElement (XmlDoc/XmlNode function)|AddElement]]</var> and <var>[[AddAttribute (XmlNode function)|AddAttribute]]</var> methods). Those operations will use the standard Unicode translation tables; there is no way to perform them using the base codepage translation tables.
</td></tr></table>
All fields are copied to the <var>XmlDoc</var> by <var>LoadFromRecord</var>.
See the [[#AddToRecord subroutine in XmlDoc class|AddToRecord subroutine description]] for an example of extracting a record to an <var>XmlDoc</var> for record copying, and see the <var>[[ToXmlDoc (Record function)|ToXmlDoc]]</var> function in the <var>[[Record class|Record]]</var> class for other examples using the method arguments.
This method was actually introduced in version 7.6 of
the <var class="product">Sirius Mods</var>, but the <var>XmlNode</var> class <var>LoadFromRecord</var> implementation prior to version 7.8 required that the <var>XmlDoc</var> 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 <var>XmlDoc</var> object according to the structure
created by the <var>[[#LoadFromRecord subroutine in XmlDoc and XmlNode classes|LoadFromRecord]]</var> subroutine.
<var>AddToRecord</var> must be contained within a &ldquo;record loop&rdquo; (for example, an <var>FRN</var>
block), and must not be contained in a fieldgroup context.
The arguments to <var>AddToRecord</var> are shown in this syntax:
<p class="code"><nowiki>%doc:AddToRecord(                          -
    [DisableFieldConstraints=bool]        -
    [,&nbsp;CopyIDs=bool]                  -
    [,&nbsp;IgnoreUndefinedFields=bool] )
</nowiki></p>
Where
<table>
<tr><th>%doc
</th><td>The method object is an <var>XmlDoc</var> whose structure conforms to that created by the <var>LoadFromRecord</var> subroutine; see [[#Structure of XmlDoc for AddToRecord|"Structure of XmlDoc for AddToRecord"]].
</td></tr>
<tr><th>DisableFieldConstraints
</th><td>This [[Methods#Named parameters|name required]] argument is a <var>[[Boolean]]</var> value; if <var>True</var>, then various field constraints (such as LENGTH-EQ, which was introduced in <var class="product">Model 204</var> V7R2) are not checked when the fields are added to the record.
The default is <var>False</var>.
</td></tr>
<tr><th>CopyIDs
</th><td>This name required argument is a <var>Boolean</var> value; if <var>True</var>, then the fieldgroup IDs are copied, if possible, from the <var>XmlDoc</var> (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 <var>False</var>, new fieldgroup IDs are generated.
The default is <var>False</var>.
</td></tr>
<tr><th>IgnoreUndefinedFields
</th><td>This name required argument is a <var>Boolean</var> value. If <var>True</var>, then: <ul> <li>If a field in the <var>XmlDoc</var> is not defined in the current file, the field is ignored. <li>If a fieldgroup in the <var>XmlDoc</var> is not defined in the current file, the fieldgroup, and all descendants of the fieldgroup element, are ignored. </ul>
If <var>False</var>, any field or fieldgroup in the <var>XmlDoc</var> that is not defined in the current file throws an <var>AddToRecordError</var> exception.
The default is <var>False</var>.
</td></tr></table>
The <var>AddToRecord</var>
subroutine can throw an <var>[[#AddToRecordError exception class|AddToRecordError]]</var> exception.
Fields in the <var>XmlDoc</var> that are defined as CAT or CTO are ignored by <var>AddToRecord</var>.
Any fieldgroup update tracking fields in a fieldgroup
are automatically set when that fieldgroup is
created by <var>AddToRecord</var>, but no other updated tracking fields are automatically
set by the updates performed by <var>AddToRecord</var>.
Any fields in the <var>XmlDoc</var> that are defined as update tracking fields are
copied to the record.
See [[#Removing update tracking fields from an XmlDoc|"Removing update tracking fields from an XmlDoc"]] for an example of removing update tracking fields from <var>AddToRecord</var>'s <var>XmlDoc</var>; 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:
<p class="code"><nowiki>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
</nowiki></p>
Some additional examples of record copying are shown in [[#Copying from multiple source records|"Copying from multiple source records"]].
Note that this method was actually introduced in version 7.6 of
the <var class="product">Sirius Mods</var>, although until version 7.8 of the <var class="product">Sirius Mods</var> an
exception was thrown if the version of <var class="product">Model 204</var> in use was older than V7R2.
===Copying from multiple source records===
Since the <var>[[#LoadFromRecord subroutine in XmlDoc and XmlNode classes|LoadFromRecord]]</var> subroutine can
extract from a record into a subtree of an <var>Element</var> node in an <var>XmlDoc</var>,
you can use it, together with <var>AddToRecord</var>, to combine multiple
source records into one target record.
As a simple example, you can put the fields from two records
&ldquo;side by side&rdquo; into a target record:
<p class="code"><nowiki>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
</nowiki></p>
The <var>XmlDoc</var> that is input to <var>AddToRecord</var> would have a structure
similar to the following:
<p class="code"><nowiki><Record ...>
  <field ...>
  ... first field from record %x
  </field>
  ...
  <field ...>
  ... first field from record %y
  </field>
  ...
</Record>
</nowiki></p>
Of course, you could also accomplish this particular objective by
using two <var>XmlDoc</var> objects:
<p class="code"><nowiki>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
</nowiki></p>
You can also use <var>LoadFromRecord</var> to modify the fieldgroup structure
within a record (using V7R2 of <var class="product">Model 204</var>).
For example, you could take the &ldquo;outer&rdquo; fields of one
record and move them to be fieldgroup members in the target
record:
<p class="code"><nowiki>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
</nowiki></p>
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 <code>SRCFILE</code> are
defined as outer fields, but in <code>TARGFILE</code> they are defined as members of fieldgroup
<code>GRP</code> (or they would need to be &ldquo;FIELDGROUP *&rdquo; fields).
So, for example, definitions for <code>SRCFILE</code> might include:
<p class="code"><nowiki>DEFINE FIELD FOO
DEFINE FIELDGROUP GRP
</nowiki></p>
and definitions for <code>TARGFILE</code> might include:
<p class="code"><nowiki>DEFINE FIELDGROUP GRP
DEFINE FIELD FOO WITH FIELDGROUP GRP
</nowiki></p>
And the <var>XmlDoc</var> that is input to the above <var>AddToRecord</var> subroutine
may look like:
<p class="code"><nowiki><Record ...>
  <fieldgroup name="GRP" ...>
      <field name="FOO">
        value of foo
      </field>
  </fieldgroup>
</Record>
</nowiki></p>
And the corresponding output of the above <code>PAI</code> would be:
<pre>
    \GRP = 1
    FOO = value of foo
    /GRP = 1
</pre>
===Removing update tracking fields from an XmlDoc===
In the simple situation, the record copying operation provided by <var>LoadFromRecord</var>
and <var>AddToRecord</var> 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 <var>AddToRecord</var>.
This can readily be accomplished by using <var>[[DeleteSubtree (XmlDoc/XmlNode subroutine)|DeleteSubtree]]</var> to remove the tracking fields you want to suppress (of course, <var>DeleteSubtree</var> could be used to remove other fields and/or fieldgroups, as well).
Consider an example in which <var>AddToRecord</var> is being used to add fields to a
record that already has fields stored in it:
<p class="code"><nowiki>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
</nowiki></p>
In this scenario, <code>GRP.UPD</code> would be set to <code>Monday</code>, overlaying the
more recent (and probably desired) value which was set on Tuesday.
To prevent this overlay, you can insert the following statement
after <code>%doc:LoadXml(RECORD.XML)</code>:
<p class="code"><nowiki>%doc:DeleteSubtree('*/fieldgroup/field[@name="GRP.UPD"]')
</nowiki></p>
===Structure of XmlDoc for AddToRecord===
The method object of the <var>AddToRecord</var> subroutine
is an <var>XmlDoc</var> whose structure conforms to that
created by the <var>LoadFromRecord</var> subroutine.
You can also create an <var>XmlDoc</var> to be used for <var>AddToRecord</var>; the rules for
the <var>XmlDoc</var> are described here.
The outline of the <var>XmlDoc</var> is:
<p class="code"><nowiki><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>
</nowiki></p>
The requirements for the <var>XmlDoc</var> are:
<table>
<tr><th>Comments and PIs
</th><td>Comment and PI nodes are allowed anywhere in the <var>XmlDoc</var>.
</td></tr>
<tr><th>Elements in non-null namespaces
</th><td>In addition to the elements mentioned below for the various elements contained in the <var>XmlDoc</var>, the <code>Record</code> and <code>fieldgroup</code> elements may have any additional child elements that are in any non-null namespace. These additional elements are ignored. For example, the following is a valid <var>AddToRecord</var> <var>XmlDoc</var> representing an empty record:
<p class="code"><nowiki><Record>
  <foo:bar xmlns:foo="u:xyz"/>
</Record>
</nowiki></p>
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 <var>AddToRecord</var> <var>XmlDoc</var>:
<p class="code"><nowiki><Record>
  <foo/>
</Record>
</nowiki></p>
</td></tr>
<tr><th>Attributes in non-null namespaces
</th><td>In addition to the attributes mentioned below for the various elements contained in the <var>XmlDoc</var>, those elements may contain any additional attributes that are in any non-null namespace. These additional attributes are ignored. For example, the following is a valid <var>AddToRecord</var> <var>XmlDoc</var> representing an empty record:
<p class="code"><nowiki><Record foo:bar="x" xmlns:foo="u:xyz"/>
</nowiki></p>
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 <var>AddToRecord</var> <var>XmlDoc</var>:
<p class="code"><nowiki><Record foo="x"/>
</nowiki></p>
</td></tr>
<tr><th>Record element
</th><td>The top level element of the <var>XmlDoc</var> must be named "Record";
all of its attributes are optional and are described below. The element children of the <code>Record</code> element are optional; they may be: <ul> <li><code>field</code> <li><code>fieldgroup</code> </ul>
The <code>Record</code> element may not have a Text child node.
</td></tr>
<tr><th>version attribute of Record element
</th><td>If present, this attribute must have the numeric value "1".
</td></tr>
<tr><th>codepage
</th><td>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 <var>XmlDoc</var> to EBCDIC.
</td></tr>
<tr><th>maxGroupID attribute of Record element
</th><td>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 <code>CopyIDs=True</code> argument is provided. This must be a non-negative integer value.
</td></tr>
<tr><th>file attribute of Record element
</th><td>This attribute is ignored.
</td></tr>
<tr><th>number attribute of Record element
</th><td>If present, this must either be "-1", signifying that the input record number is not known, or it must be a non-negative integer value. As noted in [[Compatibility/Bug fixes in V7.8#AddToRecord constraint on 'number' attribute|"AddToRecord constraint on 'number' attribute"]], this represents a compatibility issue.
</td></tr>
<tr><th>field element
</th><td>The <code>field</code> element may be a child of either the <code>Record</code> or <code>fieldgroup</code> element; it contains a field occurrence. It must have a <code>name</code> attribute, and may have an <code>encoding</code> attribute. It may have one Text child, which is either the value of the occurrence or, if the <code>encoding</code> attribute is present (with the value <code>base64</code>), is the base64 encoded value of the occurrence.
</td></tr>
<tr><th>name attribute of field element
</th><td>This attribute is required; it is the name of the field.
</td></tr>
<tr><th>encoding attribute of field element
</th><td>This attribute is optional; if present, it must have the value <code>base64</code>, indicating that the Text child of the <code>field</code> element is the base64 encoding of the field value.
</td></tr>
<tr><th>fieldgroup element
</th><td>The <code>fieldgroup</code> element may be a child of either the <code>Record</code> or <code>fieldgroup</code> element; it is the top of a subtree representing a fieldgroup occurrence.
The element children of the <code>fieldgroup</code> element are optional; they may be: <ul> <li><code>field</code> <li><code>fieldgroup</code> </ul>
The <code>fieldgroup</code> element may not have a Text child node. It must have a <code>name</code> attribute, and may have a <code>groupID</code> attribute.
</td></tr>
<tr><th>name attribute of fieldgroup element
</th><td>This attribute is required; it is the name of the fieldgroup.
</td></tr>
<tr><th>groupID attribute of fieldgroup element
</th><td>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 <code>CopyIDs=True</code> argument is provided.
</td></tr></table>
===AddToRecordError exception class===
The members of the <var>AddToRecordError</var> exception class are described below.
Except for the constructor, <var>New</var>, all class members are read-only properties:
<dl>
<dt>Reason
<dd>An enumeration of type <var>AddToRecordErrorReason</var>.
The possible values are:
<table>
<tr><th>InvalidNode
</th><td>A node in the <var>XmlDoc</var> <var>Does</var> not conform to the structure as created by the <var>LoadFromRecord</var> subroutine.
</td></tr>
<tr><th>UntranslatableFieldName
</th><td>A field name in the <var>XmlDoc</var> is not translatable to EBCDIC.
</td></tr>
<tr><th>UntranslatableFieldgroupName
</th><td>A fieldgroup name in the <var>XmlDoc</var> is not translatable to EBCDIC.
</td></tr>
<tr><th>UntranslatableValue
</th><td>A field value in the <var>XmlDoc</var> is not translatable to EBCDIC.
</td></tr>
<tr><th>InvalidBase64
</th><td>A string used for the base64 encoding of a field in the <var>XmlDoc</var> is not a valid base64 string.
</td></tr>
<tr><th>FieldNameTooLong
</th><td>A field name in the <var>XmlDoc</var> is longer than 255 characters.
</td></tr>
<tr><th>FieldgroupNameTooLong
</th><td>A fieldgroup name in the <var>XmlDoc</var> is longer than 255 characters.
</td></tr>
<tr><th>ValueTooLong
</th><td>The value of a field in the <var>XmlDoc</var> that is not defined as a LOB field in the current file is longer than 255 characters or is longer than the defined <var>LEN</var> attribute, if the field is a fixed <var>OCCURS</var> field.
</td></tr>
<tr><th>UnknownFieldName
</th><td>A field name in the <var>XmlDoc</var> is not defined in the current file.
</td></tr>
<tr><th>UnknownFieldgroupName
</th><td>A fieldgroup name in the <var>XmlDoc</var> is not defined in the current file.
</td></tr>
<tr><th>ExpectedField
</th><td>A field name in the <var>XmlDoc</var> is defined as a fieldgroup in the current file.
</td></tr>
<tr><th>ExpectedFieldgroup
</th><td>A fieldgroup name in the <var>XmlDoc</var> is defined as a field in the current file.
</td></tr>
<tr><th>ErrorAddingField
</th><td>An error occurred adding a field, such as a violation of a field constraint.
</td></tr>
<tr><th>ErrorAddingFieldgroup
</th><td>An error occurred adding a fieldgroup, such as a file full condition.
</td></tr>
<tr><th>ErrorObtainingRecord
</th><td><var>AddToRecord</var> was unable to lock the record in exclusive mode.
</td></tr>
<tr><th>InvalidFieldgroupID
</th><td>A fieldgroup ID in the <var>XmlDoc</var> is not numeric.
</td></tr>
<tr><th>InvalidCodepage
</th><td>The codepage name specified on the <code>codepage</code> attribute of the <code>Record</code> element is not a known codepage name.
</td></tr>
<tr><th>ErrorAddingMaxFieldgroupID
</th><td>The attempt to set the fieldgroup ID counter in the record failed; this is a very unusual condition.
</td></tr>
<tr><th>InsufficientStorageForLOB
</th><td><var class="product">Model 204</var> STBL, VTBL, or User Buffer storage was unavailable. The <var>Description</var> property indicates which of these is applicable.
</td></tr>
<tr><th>InvalidVersion
</th><td>Invalid value of the <code>version</code> attribute of the <code>Record</code> element; the only allowed value is "1".
</td></tr>
<tr><th>InvalidInputRecordNumber
</th><td>Invalid value of the <code>number</code> attribute of the <code>Record</code> element; it must either be "-1" or a non-negative integer.
</td></tr></table>
<dt>Description
<dd>A message that explains the error.
<dt>UntranslatableHexValue
<dd>If the <var>Reason</var> indicates that a string in the <var>XmlDoc</var> is not translatable
to EBCDIC, this contains the hexadecimal representation of the Unicode
codepoint that is not translatable.
<dt>FieldOrFieldgroupName
<dd>If the error involves a field or fieldgroup (for example,
if <code>Reason = ErrorAddingField</code>), this is the field or fieldgroup name.
<dt>NodeName
<dd>If the error involves a named node in the <var>XmlDoc</var> (for example, some cases
when <code>Reason = InvalidNode</code>), this is the name of the node.
<dt>NodeType
<dd>If the error involves a node in the <var>XmlDoc</var> (for example, all cases
when <code>Reason = InvalidNode</code>), this an <var>XmlNodeType</var> enumeration value of the
type of the node.
<dt>Value
<dd>If the error involves a value in the <var>XmlDoc</var> (for example,
when <code>Reason = InvalidBase64</code>), this is the value that is in error
(actually, up to 255 bytes of the value).
<dt>InputRecordNumber
<dd>The value of the <code>number</code> attribute of the <code>Record</code> element in the <var>XmlDoc</var>.
<dt>New
<dd>The constructor for the class, <var>New</var> lets you set values for each
member of the class:
<p class="code"><nowiki>%ex = New ( Reason = reasonEnum                  -
          [,&nbsp;Description = string]              -
          [,&nbsp;UntranslatableHexValue = hexString] -
          [,&nbsp;FieldOrFieldgroupName = string]    -
          [,&nbsp;NodeName = string]                  -
          [,&nbsp;NodeType = xmlNodeTypeEnum]        -
          [,&nbsp;Value = string]                    -
          [,&nbsp;InputRecordNumber = number])
</nowiki></p>
The <var>Reason</var> argument is required;
all other arguments are optional, [[Methods#Named parameters|name required]].
The default value of <var>InputRecordNumber</var> is "-1"; all other
default values are the null string or the Null object, as appropriate.
</dl>
Note that this class was actually introduced in version 7.6 of the <var class="product">Sirius Mods</var>.
==MoveNamespace argument for AddTopElement==
The following optional, [[Methods#Named parameters|name required]] argument has been added to the
<var>[[AddTopElement (XmlDoc function)|AddTopElement]]</var> function in the <var>XmlDoc</var> class:
<p class="code"><nowiki>MoveNamespace=boolean
</nowiki></p>
If <var>MoveNamespace</var> is <var>True</var>, and a <var>Uri</var> argument (with a non-null string) is
provided, then if the "old" top element contains a namespace
declaration that is the same as the declaration inserted due to the
<var>Uri</var> argument, the declaration is deleted from the old top element.
Note: You should use <code>MoveNamespace=True</code> 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 <code>MoveNamespace=True</code>
might trigger a scan of the entire <var>XmlDoc</var>, which <var>AddTopElement</var> does not
normally do.
==DeleteTopElement subroutine in XmlDoc class==
The syntax for <var>DeleteTopElement</var> is:
<p class="code"><nowiki>doc:DeleteTopElement
</nowiki></p>
This subroutine removes the top Element from an <var>XmlDoc</var>, and
makes its children be the &ldquo;middle siblings&rdquo; of the left and right siblings
of the deleted Element.
<ul>
<li>The deleted element may not have more than one
Element child.
<li>The deleted element may not have a Text child.
</ul>
In common usage, the Element child of the deleted Element will become the
new top Element of the <var>XmlDoc</var>.
<var>DeleteTopElement</var> is a convenient, better-performing alternative to using
<var>[[AddSubtree (XmlDoc/XmlNode function)|AddSubtree]]</var> to copy all but the top Element (or in multiple iterations,
removing successive top Elements) from an <var>XmlDoc</var>.
For example, you can use it to remove the SOAP wrappers from an <var>XmlDoc</var>.
If the contents of <code>%xdoc</code> are:
<p class="code"><nowiki><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
      <data>
        ...
      </data>
  </soap:Body>
</soap:Envelope>
</nowiki></p>
Then the following <var class="product">User Language</var> fragment:
<p class="code"><nowiki>%xdoc:DeleteTopElement
%xdoc:DeleteTopElement
</nowiki></p>
Results in the following contents of <code>%xdoc</code>:
<p class="output"><nowiki><data>
  ...
</data>
</nowiki></p>
The remaining considerations for using <var>DeleteTopElement</var>
concern the occurrence of namespace declarations in the
<var>XmlDoc</var>; there are a few details to explain how they are
manipulated, and to explain how the performance of
<var>DeleteTopElement</var> is affected.
As shown in the &ldquo;SOAP wrapper removal&rdquo; example above,
<var>DeleteTopElement</var> will remove from the <var>XmlDoc</var> any namespace
declarations (in this example, <code>xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"</code>)
that '''are not''' referenced after removing the top Element.
Note that a variant on the above example can occur using a
default namespace declaration:
<p class="code"><nowiki><Envelope xmlns"http://schemas.xmlsoap.org/soap/envelope/">
  <Body>
      <data  xmlns="">
        ...
      </data>
  </Body>
</Envelope>
</nowiki></p>
Then the following <var class="product">User Language</var> fragment:
<p class="code"><nowiki>%xdoc:DeleteTopElement
%xdoc:DeleteTopElement
</nowiki></p>
Results in the following contents of <code>%xdoc</code>:
<p class="output"><nowiki><data  xmlns="">
  ...
</data>
</nowiki></p>
As seen in this example, the null namespace declaration is not
removed, even though, strictly speaking, it is not needed.
In addition, <var>DeleteTopElement</var> will move to the new top Element
any namespace declarations that '''do''' continue to be
referenced after the top Element deletion:
<p class="code"><nowiki>%n = %d:AddElement('foo', , 'u:uri')
%n:AddElement('bar', , 'u:uri')
%d:Print
%d:DeleteTopElement
Print 'After deletion:'
%d:Print
</nowiki></p>
The result of the above <var class="product">User Language</var> fragment is:
<p class="output"><nowiki><foo xmlns="u:uri">
  <bar/>
</foo>
After deletion:
<bar xmlns="u:uri"/>
</nowiki></p>
The performance of
<var>DeleteTopElement</var> should always be significantly better than an
approach using <var>AddSubtree</var>,
and in many cases, it is constant, regardless of the
size of the <var>XmlDoc</var>.
For a detailed explanation of when the cost of <var>DeleteTopElement</var>
might vary with the size of the <var>XmlDoc</var>,
there are three reasons that the descendants
of the new top Element need to be recursively visited:
<ol>
<li>Fixing references to moved namespace declaration
<p>
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.
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>
<p class="code"><nowiki><p:oldTop xmlns:p="p:uri">
  <p:newTop>
      <child/>
  </p:newTop>
</p:oldTop>
</nowiki></p>
The <code>p:newTop</code> element needs to have it's namespace reference fixed, but that is the only reference to it, so there is no need to visit <code>child</code>.</li>
<li>Fixing pointers to a deleted namespace declaration
<p>
When a namespace declaration is deleted, it may be pointed to by other
namespace declarations (there is a graph of them in the <var>XmlDoc</var>); these
pointers need to be fixed to point to the first non-deleted
declaration following the deleted one.
This is not needed by the descendants of any element that has a
namespace declaration (in the original <var>XmlDoc</var>).
So, for example:</p>
<p class="code"><nowiki><p:oldTop xmlns:p="p:uri">
  <newTop>
      <q:child xmlns:q="q:uri">
        <grandchild/>
      </q:child>
  </newTop>
</p:oldTop>
</nowiki></p>
Both <code>newTop</code> and <code>q:child</code> need to have their namespace pointers fixed, but since <code>q:child</code> has a namespace declaration, <code>grandchild</code> doesn't need to be fixed, so it does not need to be visited.</li>
<li>Turning off &ldquo;ancestor may have non-null default namespace
declaration&rdquo;
<p>
In order to make certain namespace operations faster, there is a
"state" maintained at <var>XmlDoc</var> nodes that permits a non-null default
namespace declaration to occur on an ancestor.
This state is fixed up if <var>DeleteTopElement</var> deletes a non-null
namespace declaration (as happens in the second &ldquo;SOAP wrapper
removal&rdquo; example above, the one using a default namespace).
The need to set this state is eliminated in descendants of an Element
that have a default namespace declaration (either null or not).
So, for example:</p>
<p class="code"><nowiki><oldTop xmlns="p:uri">
  <newTop>
      <q:child xmlns="q:uri">
        <grandchild/>
      </q:child>
  </newTop>
</p:oldTop>
</nowiki></p>
The state needs to be fixed at <code>newTop</code>, but since <code>q:child</code> has
a default namespace declaration, the state at <code>grandchild</code>
does not need to be fixed up, so it does not need to be visited.
</ol>
 
{{Template:Content index: V7.8 Release Notes}}

Latest revision as of 21:34, 17 October 2013

Content moved to M204Int, but history preserved here.