Project

Profile

Help

XPDY0002: Finding root of tree: the context item is undef... » IDCP--Function.xq

Rob Oaks, 2013-01-21 17:01

 
xquery version "1.0";
(:
: Module Name: IDCP XQuery Function Library
:
: Module Version: 1.0
:
: Date: 01/15/2013
:
: XQuery
: Specification January 2007 (1.0)
:)

(:~
: This module provides a set of functions that are used by the IDCP, particularly for ACORD conversion related
: projects.
:
: @author Rob Oaks
: @version 1.0
:)

module namespace idcp-func = "http://www.itsdataservices.com/idcp/xq/func";

(:
: Note that the ACORD schema we're importing has been sliced using the ACORD schema slicer, which uses the Saxon
: processor. This allows us to reduce the size of the enormous ACORD schema to a manageable and performant size.
:)
import schema namespace acord="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" at "ACORD--Slice.xsd";
declare default element namespace "http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/";

(:~
: Under the specified root element, flatten all attributes and leaf elements into a sequence of name/value
: pair return elements, where the name is the XPath to the attribute/element relative to the specified root
: element. For example:
:
: <nodeName>id</nodeName><nodeValue>Driver_1</nodeValue>
: <nodeName>ItemIdInfo/AgencyId</nodeName><nodeValue>1</nodeValue>
: <nodeName>DriverInfo/PersonInfo/BirthDt</nodeName><nodeValue>1953-05-07</nodeValue>
: ...
: The caller can also specify a sequence of elements to be excluded from the flattening process.
: This is often used to exclude elements with multiple instances, since they can not be distinguished
: after being flattened.
:
: NOTE: This function should be treated as module private. Although XQuery has no notion of visibility, I have invented
: an @Private annotation for this purpose.
:
: @param $baseNode the node under which all attributes and leaf elements are flattened
: @param $excludedElNames the sequence of element names that specifies the excluded elements
: @param $parentNodeName flatten is a recursive function where this parameter specifies the node name from the prior
: invocation--which is the parent node name in relation to this invocation. Used to construct the XPath for the
: nodeName element. For the initial call to flatten, $parentNodeName should be the empty sequence.
: @return sequence of <nodeName>/<nodeValue> element pairs
:)
(: @Private :)
declare function idcp-func:flatten($baseNode as node(), $excludedElNames as xs:string*, $parentNodeName as xs:string?)
as element()*
{
(:Process every child attribute and child element except for elements that are specifically excluded.:)
for $node in $baseNode/(child::* | attribute::*)[not (name() = $excludedElNames)]
(:
: The node name XPath consists of the current node name appended to the parent node XPath that came from the
: previous recursion. For the initial call to flatten, $parentNodeName should be the empty sequence.
:)
let $nodeName := if ($parentNodeName) then concat($parentNodeName,"_",name($node)) else name($node)
return
if ($node/child::*) then
(:
: Since the node is not an attribute or leaf node, simply recurse without generating a return element.
:)
(idcp-func:flatten($node, $excludedElNames, $nodeName))
else
(:
: This is an attribute or leaf node, so construct a return element with nodeName and nodeValue child elements...
:)
element r
{
element nodeName {$nodeName}
,element nodeValue {data($node)}
}
(: ...and then combine it with the return from the next recursion. :)
| idcp-func:flatten($node, $excludedElNames, $nodeName)
};

(:~
: Under the specified root element, flatten all attributes and leaf elements into a sequence of name/value
: pair return elements, where the name is the XPath to the attribute/element relative to the specified root
: element. For example:
:
: <nodeName>id</nodeName><nodeValue>Driver_1</nodeValue>
: <nodeName>ItemIdInfo/AgencyId</nodeName><nodeValue>1</nodeValue>
: <nodeName>DriverInfo/PersonInfo/BirthDt</nodeName><nodeValue>1953-05-07</nodeValue>
: ...
:
: The caller can also specify a sequence of elements to be excluded from the flattening process.
: This is often used to exclude elements with multiple instances, since they can not be distinguished
: after being flattened.
:
: This function simply invokes a "private" overload that contains the actual code. This overload includes an
: additional, $parentNodeName, parameter that is used to recursively construct the XPath for the nodeName element.
:
: @param $baseNode the node under which all attributes and leaf elements are flattened
: @param $excludedElNames the sequence of element names that specifies the excluded elements
: @return sequence of <nodeName>/<nodeValue> element pairs
:)
declare function idcp-func:flatten($baseNode as node(), $excludedElNames as xs:string*)
as element()*
{
(:
: Invoke overload that does the actual work, passing empty sequence to $parentNodeName parameter as appropriate
: for initial call.
:)
idcp-func:flatten($baseNode, $excludedElNames, ())
};

(:~
: For each member of the specified node sequence, this function flattens all child attributes and leaf elements
: into a row of delimited name/value pairs. For example:
:
: id`Driver_1^ItemIdInfo/AgencyId`1^DriverInfo/PersonInfo/BirthDt`1953-05-07|
: id`Driver_2^ItemIdInfo/AgencyId`3^DriverInfo/PersonInfo/BirthDt`1959-06-25
:
: Here, there were two members of the specified node sequence, and each was flattened into a row of delimited
: data. The bactick (`) separates the name and value within a name/value pair, the caret (^) separates the name/value
: pairs, and the pipe symbol (|) separates the rows. The caller will typically parse the return data into a collection of
: maps.
: IMPORTANT: If any members of the specified node sequence contains any of the three delimiters, they must be
: escaped in some fashion or it is likely that the parsing of the return data will fail.
:
: The caller can also specify a sequence of elements to be excluded from the flattening process. This is often used to
: exclude repeated elements, since such elements can not be distinguished by name in the return data. Instead, such
: elements are typically flattened in a separate call to this function
:
: This function simply invokes a "private" overload that contains the actual code. This overload includes an
: additional, $parentNodeName, parameter that is used to recursively construct the XPath for the nodeName element.
:
: @param $baseNodes the node under which all child attributes and leaf elements are flattened
: @param $excludedElNames the sequence of element names that specifies the excluded elements
: @return one or more rows of delimited name/value pairs
:)
declare function idcp-func:nodesToDelimited($baseNodes as node()+, $excludedElNames as xs:string*)
as xs:string*
{
(: The row, column, and pair delimiters :)
let $rowDelim as xs:string := "|"
let $colDelim as xs:string := "^"
let $pairDelim as xs:string := "`"
for $baseNode as element() in $baseNodes
let $isLastNode as xs:boolean := $baseNode is $baseNodes[last( )]

(: Flatten the current base node into a sequence of name/value pair return elements.:)
let $row as element()* := idcp-func:flatten($baseNode, $excludedElNames)

(: Now, convert the returned elements into a row of delimited name/value pairs.:)
for $colPair in $row
let $isLastColPair as xs:boolean := $colPair is $row[last( )]
(:
: Return the delimited name/value pair, followed by the column delimiter or, if it's the last pair in the row,
: either the row delimiter (if there are more rows) or no delimiter (if there are no more rows).
:)
(:return (concat($colPair/data(nodeName),$pairDelim,$colPair/data(nodeValue),
if ($isLastColPair ) then
if (not($isLastNode)) then ($rowDelim) else ()
else
($colDelim))):)
return (concat($colPair/data(nodeName),$pairDelim,$colPair/data(nodeValue),
if (not($isLastColPair)) then
($colDelim)
else
()))
};
(1-1/5)