Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dscape/364356 to your computer and use it in GitHub Desktop.
Save dscape/364356 to your computer and use it in GitHub Desktop.
Generate XML out of XPath Expressions
import module namespace mem = "http://xqdev.com/in-mem-update"
at "/MarkLogic/appservices/utils/in-mem-update.xqy" ;
declare variable $DEBUG as xs:boolean := fn:true() ;
(: hack because the inmem update looses the right focus
: if you have an idea on a workaround, please let me know
:)
declare variable $redo as xs:string* := ();
(: functional ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::)
declare function local:fst( $l ) { $l[1] } ;
declare function local:snd( $l ) { $l[2] } ;
declare function local:head( $l ) { local:fst( $l ) } ;
declare function local:tail( $l ) { fn:subsequence( $l, 2 ) } ;
declare function local:fold($f, $z, $l) {
if( fn:empty( $l ) ) then $z else
local:fold( $f,
xdmp:apply( $f, $z, local:head( $l ) ),
local:tail( $l ) ) } ;
declare function local:head-two( $l ) { fn:subsequence( $l, 1, 2 ) } ;
declare function local:tail-two( $l ) { fn:subsequence( $l, 3 ) } ;
declare function local:fold2($f, $z, $l) {
if( fn:empty( $l ) ) then $z else
local:fold2( $f,
xdmp:apply( $f, $z, local:head-two( $l ) ),
local:tail-two( $l ) ) } ;
(:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::)
declare function local:log( $string ) {
if ($DEBUG) then xdmp:log ( $string ) else () } ;
declare function local:replace-value( $node, $value ) {
if( xdmp:node-kind($node) = "attribute" )
then mem:node-replace( $node,
attribute { fn:node-name( $node ) } { $value } )
else mem:node-replace( $node,
element { fn:node-name( $node ) } { $value } ) } ;
declare function local:insert-child( $parent, $child ) {
if( xdmp:node-kind($child) = "attribute" )
then mem:node-insert-before( ($parent/*)[1] , $child )
else mem:node-insert-child( $parent , $child ) } ;
declare function local:create-nodes( $current-node, $xpath-step ) {
let $current-node-name := $current-node /fn:local-name(.)
let $new-node :=
if ( fn:starts-with( $xpath-step, "@") )
then attribute { fn:substring( $xpath-step, 2 ) } {}
else element { $xpath-step } {}
let $new-node-name := $new-node /fn:local-name(.)
let $steps := fn:string-join( local:tail( $redo ), "/" )
let $child :=
if( $steps )
then
let $_ := xdmp:log( " ~~steps" )
return xdmp:unpath( fn:concat( "$current-node/", $steps ) )
else
let $_ := xdmp:log( " ~~no-steps" )
return xdmp:unpath( fn:concat( "$current-node/", $xpath-step ) )
let $_ := xdmp:set( $redo, ( $redo, $xpath-step ) )
let $_ := local:log( " .................." )
let $_ := local:log( " local:create-nodes" )
let $_ := local:log( fn:concat(" r=", $steps ) )
let $_ := local:log( fn:concat(" c=", xdmp:quote($current-node)) )
let $_ := local:log( fn:concat(" s=", $xpath-step) )
let $_ := local:log( fn:concat(" cn=", xdmp:quote( $current-node-name ) ) )
let $_ := local:log( fn:concat(" n=", xdmp:quote( $new-node ) ) )
let $_ := local:log( fn:concat(" nn=", xdmp:quote( $new-node-name ) ) )
let $_ := local:log( fn:concat(" t=", $steps ) )
let $_ := local:log( fn:concat(" ch=", xdmp:quote( $child ) ) )
return if( fn:empty( $current-node ) )
then $new-node
else if( $current-node-name = $new-node-name )
then $current-node
else
if ( $steps )
then
if ( $child )
then local:insert-child( $child , $new-node )
else local:insert-child( $current-node , $new-node )
else
if( $child )
then $child
else local:insert-child( $current-node , $new-node ) } ;
declare function local:generate-tree-step( $current-tree, $xpath ) {
let $_ := local:log( " ..................." )
let $_ := local:log( " local:generate-tree-step" )
let $_ := local:log( fn:concat( " z=", xdmp:quote($current-tree) ) )
let $_ := local:log( fn:concat( " p=", fn:string-join($xpath, " ") ) )
let $_ := xdmp:set($redo, ())
let $f := xdmp:function( xs:QName( 'local:create-nodes' ) )
return local:fold( $f, $current-tree,
local:tail( fn:tokenize( $xpath, "/" ) ) ) } ;
declare function local:populate-tree-step ( $current-tree, $pair ) {
let $xpath := local:fst( $pair )
let $value := local:snd( $pair )
let $relative-path := fn:replace($xpath, "^/\w+(/.*)", "$1")
let $selected :=
xdmp:unpath( fn:concat( "$current-tree", $relative-path ) )
let $_ := local:log( fn:concat( " -x = ", $xpath ) )
let $_ := local:log( fn:concat( " -s = ", xdmp:quote($selected) ) )
let $_ := local:log( fn:concat( " -v = ", $value ) )
let $updated := local:replace-value( $selected, $value )
let $_ := local:log( fn:concat( " -u= ", xdmp:quote($updated) ) )
return $updated } ;
declare function local:generate-tree( $xpaths ) {
let $_ := local:log( " local:generate-tree" )
let $_ := local:log( fn:concat( " lst=", fn:string-join( $xpaths, ", ") ) )
let $f := xdmp:function( xs:QName( 'local:generate-tree-step' ) )
return local:fold( $f, (), $xpaths ) } ;
declare function local:populate-tree( $tree, $xpaths, $values ) {
let $pairs := for $xpath at $i in $xpaths return ( $xpath, $values[$i] )
let $f := xdmp:function( xs:QName( 'local:populate-tree-step' ) )
let $_ := local:log( " local:populate-tree" )
let $_ := local:log( fn:concat( " t=", xdmp:quote($tree) ) )
let $_ := local:log( fn:concat( " p=", fn:string-join( $pairs, ", ") ) )
return document { local:fold2( $f, $tree, $pairs ) } } ;
declare function local:process-fields( $xpaths, $values ) {
let $_ := local:log( "# start ############################################" )
let $tree := local:generate-tree( $xpaths )
return local:populate-tree( $tree, $xpaths, $values ) } ;
local:process-fields(
( "/person/name", "/person/address/zipcode", "/person/address/street", "/person/phone", "/person/@id" ),
( "John Doe", "10020", "barker", "555 432-4533", "555" ) )
import module namespace mem = "http://xqdev.com/in-mem-update"
at "/MarkLogic/appservices/utils/in-mem-update.xqy" ;
(: hack because the inmem update looses the right focus
: if you have an idea on a workaround, please let me know
:)
declare variable $redo as xs:string* := ();
(: functional ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::)
declare function local:fst( $l ) { $l[1] } ;
declare function local:snd( $l ) { $l[2] } ;
declare function local:head( $l ) { local:fst( $l ) } ;
declare function local:tail( $l ) { fn:subsequence( $l, 2 ) } ;
declare function local:fold($f, $z, $l) {
if( fn:empty( $l ) ) then $z else
local:fold( $f,
xdmp:apply( $f, $z, local:head( $l ) ),
local:tail( $l ) ) } ;
declare function local:head-two( $l ) { fn:subsequence( $l, 1, 2 ) } ;
declare function local:tail-two( $l ) { fn:subsequence( $l, 3 ) } ;
declare function local:fold2($f, $z, $l) {
if( fn:empty( $l ) ) then $z else
local:fold2( $f,
xdmp:apply( $f, $z, local:head-two( $l ) ),
local:tail-two( $l ) ) } ;
(:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::)
declare function local:replace-value( $node, $value ) {
if( xdmp:node-kind($node) = "attribute" )
then mem:node-replace( $node,
attribute { fn:node-name( $node ) } { $value } )
else mem:node-replace( $node,
element { fn:node-name( $node ) } { $value } ) } ;
declare function local:insert-child( $parent, $child ) {
if( xdmp:node-kind($child) = "attribute" )
then mem:node-insert-before( ($parent/*)[1] , $child )
else mem:node-insert-child( $parent , $child ) } ;
declare function local:create-nodes( $current-node, $xpath-step ) {
let $current-node-name := $current-node /fn:local-name(.)
let $new-node :=
if ( fn:starts-with( $xpath-step, "@") )
then attribute { fn:substring( $xpath-step, 2 ) } {}
else element { $xpath-step } {}
let $new-node-name := $new-node /fn:local-name(.)
let $steps := fn:string-join( local:tail( $redo ), "/" )
let $child :=
if( $steps )
then xdmp:unpath( fn:concat( "$current-node/", $steps ) )
else xdmp:unpath( fn:concat( "$current-node/", $xpath-step ) )
let $_ := xdmp:set( $redo, ( $redo, $xpath-step ) )
return if( fn:empty( $current-node ) )
then $new-node
else if( $current-node-name = $new-node-name )
then $current-node
else
if ( $steps )
then
if ( $child )
then local:insert-child( $child , $new-node )
else local:insert-child( $current-node , $new-node )
else
if( $child )
then $child
else local:insert-child( $current-node , $new-node ) } ;
declare function local:generate-tree-step( $current-tree, $xpath ) {
let $_ := xdmp:set($redo, ())
let $f := xdmp:function( xs:QName( 'local:create-nodes' ) )
return local:fold( $f, $current-tree,
local:tail( fn:tokenize( $xpath, "/" ) ) ) } ;
declare function local:populate-tree-step ( $current-tree, $pair ) {
let $xpath := local:fst( $pair )
let $value := local:snd( $pair )
let $relative-path := fn:replace($xpath, "^/\w+(/.*)", "$1")
let $selected :=
xdmp:unpath( fn:concat( "$current-tree", $relative-path ) )
let $updated := local:replace-value( $selected, $value )
return $updated } ;
declare function local:generate-tree( $xpaths ) {
let $f := xdmp:function( xs:QName( 'local:generate-tree-step' ) )
return local:fold( $f, (), $xpaths ) } ;
declare function local:populate-tree( $tree, $xpaths, $values ) {
let $pairs := for $xpath at $i in $xpaths return ( $xpath, $values[$i] )
let $f := xdmp:function( xs:QName( 'local:populate-tree-step' ) )
return document { local:fold2( $f, $tree, $pairs ) } } ;
declare function local:process-fields( $xpaths, $values ) {
let $tree := local:generate-tree( $xpaths )
return local:populate-tree( $tree, $xpaths, $values ) } ;
local:process-fields(
( "/person/name", "/person/address/zipcode", "/person/address/street", "/person/phone", "/person/@id" ),
( "John Doe", "10020", "barker", "555 432-4533", "555" ) )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment