Transforming flat xml to nested xml using Xquery and recursive functions - xml-serialization

I'm troubled with an exercise on recursive functions in Xquery. I need to transform a flat XML tree to a nested XML tree using only Xquery.
The flat XML looks like this:
<?xml version="1.0"?>
<tree>
<node id="1" type="Folder">
<child>2</child>
<child>3</child>
</node>
<node id="2" type="Folder">
<child>4</child>
</node>
<node id="3" type="Folder">
<child>5</child>
<child>6</child>
</node>
<node id="4" type="Item" />
<node id="5" type="Folder">
<child>7</child>
</node>
<node id="6" type="Item" />
<node id="7" type="Item" />
</tree>
The nested XML required looks like this:
<?xml version="1.0"?>
<tree>
<node id="1" type="Folder" children="2 3">
<node id="2" type="Folder">
<node id="4" type="Item" />
</node>
<node id="3" type="Folder">
<node id="5" type="Folder" children="7">
<node id="7" type="Item" />
</node>
<node id="6" type="Item" />
</node>
</node>
</tree>
I've tried to Xquery this without recursive functions, but without much luck. Especially the conditioning seems strange to me; the root element of the new nested XML should be the node with id="1", since it does not exist as a child of any other element. However, if I try to specify this as a condition, e.g. below, it does not seem possible to just select that node.
for $node in /Tree/node[#id != /Tree/node/child]
return
<node id="{data($node/#id)}" type="{data($node/#type)}">
(: Lower level subqueries.... :)
</node>
UPDATE: I've come a lot further, but now I'm stuck with the [condition] on the selection of nodes that have an id equal to the contents of a parent node, i.e. the for statement in the recursive function does not give back any nodes, which is unexpected.
This is my code thus far:
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace local = "localhost";
declare option output:method "xml";
declare option output:omit-xml-declaration "no";
declare option output:indent "yes";
declare option output:doctype-system "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
declare function local:addSubNode($n)
{
for $subnode in doc("xml/flat-tree.xml")/tree/node[#id = $n]
let $subnid:=data($subnode/#id)
let $subtype:=data($subnode/#type)
return <node id="{$subnid}" type="{$subtype}">
{local:addSubNode($subnid)}
</node>
};
<tree>
{
for $node in doc("xml/flat-tree.xml")/tree/node[not(#id = /tree/node/child)]
let $nid:=data($node/#id)
return <node id="{$nid}" type="{data($node/#type)}">
{for $child in $node
let $cid:=data($child)
return local:addSubNode($cid)
}
</node>
}
</tree>

Just a partial answer - = and != can compare an atomic value against a sequence, but it works as a logical OR against each term. That is "a = b" means "is there a value in b that equals a" and "a != b" means "is there a value in b that does not equal a", which is not what you're looking for. You want "is there no value in b which equals a" which is "not(a = b)".
for $node in $d/node[not(#id = $d/node/child)] return
<node id="{$node/#id}" type="{$node/#type}">
(: etc. :)
</node>

Ok got it! Eventually I fed the whole $node or $child element to the recursive function instead of just its id attribute, now it works!
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace local = "localhost";
declare option output:method "xml";
declare option output:omit-xml-declaration "no";
declare option output:indent "yes";
declare option output:doctype-system "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
(:lookupChildren function :)
declare function local:lookupChildren($p)
{
for $child in $p/child
return $child
};
declare function local:addSubNode($n)
{
for $subnode in doc("xml/flat-tree.xml")/tree/node
let $subnid:=data($subnode/#id)
let $subtype:=data($subnode/#type)
where $subnode/#id = $n/child
return
<node id="{$subnid}" type="{$subtype}" children="{local:lookupChildren($subnode)}">
{local:addSubNode($subnode)}
</node>
};
<tree>
{
for $node in doc("xml/flat-tree.xml")/tree/node[not(#id = /tree/node/child)]
let $nid:=data($node/#id)
let $ntype:=data($node/#type)
return <node id="{$nid}" type="{$ntype}" children="{local:lookupChildren($node)}">
{local:addSubNode($node)}
</node>
}
</tree>

Related

PowerShell script returning node count of 0

I am trying to use PowerShell to count the number of operation nodes in my XML file.
[xml]$doc = [xml]#'
<?xml version="1.0" encoding="utf-8"?>
<DeploymentReport xmlns="http://schemas.microsoft.com/sqlserver/dac/DeployReport/2012/02"><Alerts />
<Operations>
<Operation Name="Alter">
<Item />
<Item />
<Item />
</Operation>
</Operations>
</DeploymentReport>
'#
$info = $doc.SelectNodes("/DeploymentReport/Operations/Operation")
$info.count
However, $info.count is always returning 0. What am I doing wrong?
You're not selecting the nodes with the correct namespace. You'd need to do something like:
[Xml] $Doc = [Xml]#'
<?xml version="1.0" encoding="utf-8"?>
<DeploymentReport xmlns="http://schemas.microsoft.com/sqlserver/dac/DeployReport/2012/02">
<Alerts/>
<Operations>
<Operation Name="Alter">
<Item/>
<Item/>
<Item/>
</Operation>
</Operations>
</DeploymentReport>
'#
$NSM = New-Object System.Xml.XmlNamespaceManager $Doc.NameTable
$NSM.AddNamespace("dr", "http://schemas.microsoft.com/sqlserver/dac/DeployReport/2012/02")
$Info = $Doc.SelectNodes("/dr:DeploymentReport/dr:Operations/dr:Operation", $NSM)
$Info.Count
1

DGML - Add weight to link

How do I add weight or value to the Edges or Link in a DGML file?
<?xml version='1.0' encoding='utf-8'?>
<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="a" Label="a" Size="10" />
<Node Id="b" Background="#FF008080" Label="b" />
<Node Id="c" Label="c" Start="2010-06-10" />
</Nodes>
<Links>
<Link Source="a" Target="b" />
<Link Source="a" Target="c" />
</Links>
<Properties>
<Property Id="Background" Label="Background" DataType="Brush" />
<Property Id="Label" Label="Label" DataType="String" />
<Property Id="Size" DataType="String" />
<Property Id="Start" DataType="DateTime" />
</Properties>
</DirectedGraph>
I would like to be able to assign a weight or value to the lines between each node to designate the strength between the nodes.
You can add weights to each link by adding a label field with a value to each of the Link Sources. The numbers will appear beside the arrows on your graph.
<Link Source="a" Target="b" Label="5" />
<Link Source="a" Target="c" Label="6" />
Additionally, the background color of each node can be changed by creating Category groups and assigning that group to each node.
<Category Id="Orange" Background="Orange" />
<Category Id="Yellow" Background="Yellow" />
<Node Id="a" Category="Orange" />
<Node Id="b" Category="Yellow" />
Here's an example that uses a link Weight Style to do it:
<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Banana" UseManualLocation="True" />
<Node Id="Test" UseManualLocation="True" />
</Nodes>
<Links>
<Link Source="Test" Target="Banana" Priority="10"/>
<Link Source="Test" Target="Green" />
</Links>
<Properties>
<Property Id="Bounds" DataType="System.Windows.Rect" />
<Property Id="UseManualLocation" DataType="System.Boolean" />
</Properties>
<Styles>
<Style TargetType="Link">
<Setter Property="Weight" Expression="Priority" />
</Style>
</Styles>
</DirectedGraph>

Find and replace a text in a XML file between two placeholders with a placeholder having special characters

I have a requirement mentioned below.
Sample file.txt ::
<?xml version='1.0' encoding='utf-8'?>
<Server port="${shutdown.port}" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="com.springsource.tcserver.serviceability.rmi.JmxSocketListener"
port="${jmx.port}"
bind="127.0.0.1"
useSSL="false"
passwordFile="${catalina.base}/conf/jmxremote.password"
accessFile="${catalina.base}/conf/jmxremote.access"
authenticate="true"/>
<Listener className="com.springsource.tcserver.serviceability.deploy.TcContainerDeployer" />
<GlobalNamingResources>
<Resource
name="jdbc/myDBPool1"
auth="Container"
type="oracle.jdbc.pool.OracleDataSource"
description="Oracle Datasource"
factory="oracle.jdbc.pool.OracleDataSourceFactory"
url="jdbc:oracle:thin:#localhost:<dbanme>"
user="myusername"
password="somepassword1"
validationQuery="SELECT 1 FROM DUAL"
/>
<Resource
name="jdbc/myDBPool2"
auth="Container"
type="oracle.jdbc.pool.OracleDataSource"
description="Oracle Datasource"
factory="oracle.jdbc.pool.OracleDataSourceFactory"
url="jdbc:oracle:thin:#localhost:<dbanme>"
user="myusername"
password="somepassword2"
validationQuery="SELECT 1 FROM DUAL"
/>
<Resource
name="jdbc/myDBPool3"
auth="Container"
type="oracle.jdbc.pool.OracleDataSource"
description="Oracle Datasource"
factory="oracle.jdbc.pool.OracleDataSourceFactory"
url="jdbc:oracle:thin:#localhost:<dbanme>"
user="myusername"
password="somepassword3"
validationQuery="SELECT 1 FROM DUAL"
/>
</GlobalNamingResources>
<Service name="Catalina">
<Executor name="tomcatThreadPool" namePrefix="tomcat-http--" maxThreads="300" minSpareThreads="50"/>
<Connector executor="tomcatThreadPool"
port="${http.port}"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="${https.port}"
acceptCount="100"
maxKeepAliveRequests="15"/>
<Engine name="Catalina" defaultHost="localhost">
<!--
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
-->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true" deployOnStartup="true" deployXML="true"
xmlValidation="false" xmlNamespaceAware="false">
</Host>
</Engine>
</Service>
</Server>
I am trying this script to find some text( .password. ) between two strings (inclusive of strings) and then replace it with a placeholder. Whenever my cmdlinepasswd placeholder has special characters say "/" character it fails.
sed -ie "/$datasource/,/password*/ {s/.*password.*/\tpassword=\"$cmdlinepasswd\"/;}" file.txt
assuming
datasource=jdbc/myDBPool1
cmdlinepasswd=new/passwd
I am new to scripting and any help would be appreciated.
Thank you.
This should do it:
awk -v RS= -v ORS='\n\n' -v datasource="jdbc/myDBPool1" -v cmdlinepasswd="new/passwd" '
$0 ~ "^[[:space:]]*<Resource.*name=\"" datasource "\"" {
sub(/password="[^"]+"/,"password=\"" cmdlinepasswd "\"")
} 1' file
It just looks for records that start with <Resource followed by name="<your datasource variable value>" and if it finds one it replaces the password in that record.
If you have shell variables already
datasource="jdbc/myDBPool1"
cmdlinepasswd="new/passwd"
just pass them into awk as:
awk -v RS= -v ORS='\n\n' -v datasource="$datasource" -v cmdlinepasswd="$cmdlinepasswd" '
$0 ~ "^[[:space:]]*<Resource.*name=\"" datasource "\"" {
sub(/password="[^"]+"/,"password=\"" cmdlinepasswd "\"")
} 1' file
One way using perl. It accepts three arguments, the variables to check/substitute and the input file. It's better than sed because it does quotemeta (\Q) to arguments, that escapes many special characters of a regex.
perl -pe '
## Get arguments but input file.
BEGIN { ($datasource,$cmdlinepasswd) = (shift,shift) }
## Get a range from resource name until the line with the password.
if ( $range = ( m/=\s*"\Q${datasource}"/ ... /password\s*=/ ) ) {
## "E0" points to last line (that contains the password, so
## change it.
if ( q|E0| eq substr $range, -2 ) {
s/(=\s*").*("\s*)$/$1${cmdlinepasswd}$2/;
}
}
' "$datasource" "$cmdlinepasswd" infile
With your example input data, it yields (only showed the part of the password that was modified, and left the rest untouched):
<?xml version='1.0' encoding='utf-8'?>
<Server port="${shutdown.port}" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="com.springsource.tcserver.serviceability.rmi.JmxSocketListener"
port="${jmx.port}"
bind="127.0.0.1"
useSSL="false"
passwordFile="${catalina.base}/conf/jmxremote.password"
accessFile="${catalina.base}/conf/jmxremote.access"
authenticate="true"/>
<Listener className="com.springsource.tcserver.serviceability.deploy.TcContainerDeployer" />
<GlobalNamingResources>
<Resource
name="jdbc/myDBPool1"
auth="Container"
type="oracle.jdbc.pool.OracleDataSource"
description="Oracle Datasource"
factory="oracle.jdbc.pool.OracleDataSourceFactory"
url="jdbc:oracle:thin:#localhost:<dbanme>"
user="myusername"
password="new/passwd"
validationQuery="SELECT 1 FROM DUAL"
/>
...
NOTE: After see your edit, it would be a better option to use an xml parser, but this solution seems to work in my test.

add xml attribute "xsi:nil"

I am reading the following file in powershell.
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<nested1>
<level1 xsi:nil="true" />
<level2>2</level2>
</nested1>
<nested2>
<level1 xsi:nil="true" />
<level2>2</level2>
</nested2>
</root>
using...
[xml]$XmlDoc = get-content $XMLFile
I would like to set
$XmlDoc.root.nested1.level2
so it has the attribute xsi:nil="true"
so the file appears as
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<nested1>
<level1 xsi:nil="true" />
<level2 xsi:nil="true" />
</nested1>
<nested2>
<level1 xsi:nil="true" />
<level2>2</level2>
</nested2>
</root>
Many Thanks for any advice offered.
Use SetAttribute() and provide the namespace URI.
$node = $XmlDoc.SelectSingleNode('//nested1/level2')
$node.SetAttribute('nil', 'http://www.w3.org/2001/XMLSchema-instance', 'true') |
Out-Null

OpenJPA orphan removal not working

I am trying out a master child test case with a OneToMany unidirectional relationship.
I have a scenario where I want to update the master by removing some entries from the children list. When I do a merge, I want these child entries to be automatically deleted.
I have tried in vain to get this working. I tried orphanremoval too, but that too didn't help. There were some suggestions to add equals and hashcode, which didn't work either.
Any hint or help in this regard is highly appreciated. Thank you.
I am using JPA2.0 with OpenJPA 2.1.0
Here is my code.
//Parent class
public class Account {
private String accountId;
private String accountNumber;
private List<SubAccount> subAccounts;
//followed by getters/setters
}
//Child class
public class SubAccount {
private String subAccountId;
private String subAccountNumber;
//followed by getters/setters
}
My orm.xml is as follows.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<entity-mappings version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">
<entity name="Account" class="test.data.Account">
<table name="ACCOUNT" />
<sequence-generator name="AccountSeq"
sequence-name="ACCOUNT_TEST_SEQ" />
<attributes>
<id name="accountId">
<column name="ACCOUNT_ID" />
<generated-value strategy="SEQUENCE" generator="AccountSeq" />
</id>
<basic name="accountNumber">
<column name="ACCOUNT_NO" />
</basic>
<one-to-many name="subAccounts" fetch="EAGER"
target-entity="test.data.SubAccount" orphan-removal="true" >
<join-column name="ACCOUNT_ID" referenced-column-name="ACCOUNT_ID" nullable="false" updatable="true"/>
<cascade>
<cascade-all />
</cascade>
</one-to-many>
</attributes>
</entity>
<entity name="SubAccount" class="test.data.SubAccount">
<table name="SUBACCOUNT" />
<sequence-generator name="SubAccountSeq"
sequence-name="SUBACCOUNT_TEST_SEQ" />
<attributes>
<id name="subAccountPK">
<column name="SUBACCOUNT_ID" />
<generated-value strategy="SEQUENCE" generator="AccountSeq" />
</id>
<basic name="subAccountNumber">
<column name="SUBACCOUNT_NO" />
</basic>
</attributes>
</entity>
</entity-mappings>
And this is what I am trying to do.
Account acc = (Account)entityMgr.find(Account.class, "1100")
acc.getSubAccounts().remove(0);
entityMgr.merge(acc)
I expect the subaccount to be deleted.
OpenJPA overrides the orphan-removal behavior if the cascade type is set to cascade-all or cascade-remove.
Instead of cascade-all, I had to configure cascade-type with cascade-persist, cascade-merge, cascade-refresh and cascade-detach.