Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 00:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/9753127 to your computer and use it in GitHub Desktop.
Save bennadel/9753127 to your computer and use it in GitHub Desktop.
Deleting XML Node Arrays From A ColdFusion XML Document
<!--- Create a ColdFusion XML document. --->
<cfxml variable="xmlGirls">
<girls>
<girl id="1">
<name>Hayden Panettiere</name>
<hair>Blonde</hair>
</girl>
<girl id="2">
<name>Christina Cox</name>
<hair>Blonde</hair>
</girl>
<girl id="3">
<name>Winona Ryder</name>
<hair>Brunette</hair>
</girl>
<girl id="4">
<name>Minnie Driver</name>
<hair>Brunette</hair>
</girl>
<girl id="5">
<name>Julia Stiles</name>
<hair>Blonde</hair>
</girl>
</girls>
</cfxml>
<!--- Query for all blonde xml nodes. --->
<cfset arrBlondeGirls = XmlSearch(
xmlGirls,
"//girl[ hair/text() = 'Blonde' ]"
) />
<!--- Delete all blondes from the document. --->
<cfset XmlDeleteNodes(
xmlGirls,
arrBlondeGirls
) />
<!---
Output the modified XML document, which should, at
this point, only contain Brunetted.
--->
<cfdump
var="#xmlGirls#"
label="xmlGirls - After Blondes Have Been Removed"
/>
<cffunction
name="XmlDeleteNodes"
access="public"
returntype="void"
output="false"
hint="I remove a node or an array of nodes from the given XML document.">
<!--- Define arugments. --->
<cfargument
name="XmlDocument"
type="any"
required="true"
hint="I am a ColdFusion XML document object."
/>
<cfargument
name="Nodes"
type="any"
required="false"
hint="I am the node or an array of nodes being removed from the given document."
/>
<!--- Define the local scope. --->
<cfset var LOCAL = StructNew() />
<!---
Check to see if we have a node or array of nodes. If we
only have one node passed in, let's create an array of
it so we can assume an array going forward.
--->
<cfif NOT IsArray( ARGUMENTS.Nodes )>
<!--- Get a reference to the single node. --->
<cfset LOCAL.Node = ARGUMENTS.Nodes />
<!--- Convert single node to array. --->
<cfset ARGUMENTS.Nodes = [ LOCAL.Node ] />
</cfif>
<!---
Flag nodes for deletion. We are going to need to delete
these via the XmlChildren array of the parent, so we
need to be able to differentiate them from siblings.
Also, we only want to work with actual ELEMENT nodes,
not attributes or anything, so let's remove any nodes
that are not element nodes.
--->
<cfloop
index="LOCAL.NodeIndex"
from="#ArrayLen( ARGUMENTS.Nodes )#"
to="1"
step="-1">
<!--- Get a node short-hand. --->
<cfset LOCAL.Node = ARGUMENTS.Nodes[ LOCAL.NodeIndex ] />
<!---
Check to make sure that this node has an XmlChildren
element. If it does, then it is an element node. If
not, then we want to get rid of it.
--->
<cfif StructKeyExists( LOCAL.Node, "XmlChildren" )>
<!--- Set delet flag. --->
<cfset LOCAL.Node.XmlAttributes[ "delete-me-flag" ] = "true" />
<cfelse>
<!---
This is not an element node. Delete it from out
list of nodes to delete.
--->
<cfset ArrayDeleteAt(
ARGUMENTS.Nodes,
LOCAL.NodeIndex
) />
</cfif>
</cfloop>
<!---
Now that we have flagged the nodes that need to be
deleted, we can loop over them to find their parents.
All nodes should have a parent, except for the root
node, which we cannot delete.
--->
<cfloop
index="LOCAL.Node"
array="#ARGUMENTS.Nodes#">
<!--- Get the parent node. --->
<cfset LOCAL.ParentNodes = XmlSearch( LOCAL.Node, "../" ) />
<!---
Check to see if we have a parent node. We can't
delete the root node, and we also be deleting other
elements as well - make sure it is all playing
nicely together. As a final check, make sure that
out parent has children (only happens if we are
dealing with the root document element).
--->
<cfif (
ArrayLen( LOCAL.ParentNodes ) AND
StructKeyExists( LOCAL.ParentNodes[ 1 ], "XmlChildren" )
)>
<!--- Get the parent node short-hand. --->
<cfset LOCAL.ParentNode = LOCAL.ParentNodes[ 1 ] />
<!---
Now that we have a parent node, we want to loop
over it's children to one the nodes flagged as
deleted (and delete them). As we do this, we
want to loop over the children backwards so that
we don't go out of bounds as we start to remove
child nodes.
--->
<cfloop
index="LOCAL.NodeIndex"
from="#ArrayLen( LOCAL.ParentNode.XmlChildren )#"
to="1"
step="-1">
<!--- Get the current node shorthand. --->
<cfset LOCAL.Node = LOCAL.ParentNode.XmlChildren[ LOCAL.NodeIndex ] />
<!---
Check to see if this node has been flagged
for deletion.
--->
<cfif StructKeyExists( LOCAL.Node.XmlAttributes, "delete-me-flag" )>
<!--- Delete this node from parent. --->
<cfset ArrayDeleteAt(
LOCAL.ParentNode.XmlChildren,
LOCAL.NodeIndex
) />
<!---
Clean up the node by removing the
deletion flag. This node might still be
used by another part of the program.
--->
<cfset StructDelete(
LOCAL.Node.XmlAttributes,
"delete-me-flag"
) />
</cfif>
</cfloop>
</cfif>
</cfloop>
<!--- Return out. --->
<cfreturn />
</cffunction>
<cffunction
name="XmlDeleteNodesJava"
access="public"
returntype="void"
output="false"
hint="I remove a node or an array of nodes from the given XML document.">
<!--- Define arugments. --->
<cfargument
name="XmlDocument"
type="any"
required="true"
hint="I am a ColdFusion XML document object."
/>
<cfargument
name="Nodes"
type="any"
required="false"
hint="I am the node or an array of nodes being removed from the given document."
/>
<!--- Define the local scope. --->
<cfset var LOCAL = StructNew() />
<!---
Check to see if we have a node or array of nodes. If we
only have one node passed in, let's create an array of
it so we can assume an array going forward.
--->
<cfif NOT IsArray( ARGUMENTS.Nodes )>
<!--- Get a reference to the single node. --->
<cfset LOCAL.Node = ARGUMENTS.Nodes />
<!--- Convert single node to array. --->
<cfset ARGUMENTS.Nodes = [ LOCAL.Node ] />
</cfif>
<!--- Loop over the nodes. --->
<cfloop
index="LOCAL.Node"
array="#ARGUMENTS.Nodes#">
<!--- Get the parent node. --->
<cfset LOCAL.ParentNode = LOCAL.Node.GetParentNode() />
<!---
Check to see if the parent was found. If not, then
we are not dealing with an Element node.
--->
<cfif StructKeyExists( LOCAL, "ParentNode" )>
<!---
Get the previous sibling of the node in the
question. If there is no previous sibling, this
will return NULL and it will tell us that
the target node is the first node in the child
nodes array.
--->
<cfset LOCAL.PrevNode = LOCAL.Node.GetPreviousSibling() />
<!---
Check to see if the previous node was found
or if is null (which will have removed the
struct key).
--->
<cfif StructKeyExists( LOCAL, "PrevNode" )>
<!---
We have the prev node. Use that to get the
Java version of our ChildNode and delete from
the parent.
--->
<cfset LOCAL.ParentNode.RemoveChild(
LOCAL.PrevNode.GetNextSibling()
) />
<cfelse>
<!---
The previous node didn't exist which means
that our target node is the first node in the
child array.
--->
<cfset LOCAL.ParentNode.RemoveChild(
LOCAL.ParentNode.GetFirstChild()
) />
</cfif>
</cfif>
</cfloop>
<!--- Return out. --->
<cfreturn />
</cffunction>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment