|
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
|
|
()))
|
|
};
|