developers

    Forum Stats

  • 3,873,880 Users
  • 2,266,627 Discussions
  • 7,911,650 Comments

Discussions

Parent-Child Unbound Transformation from Linear Structure

vv*338251*hu
vv*338251*hu Member Posts: 49
edited May 23, 2013 1:19AM in XQuery
Hi ,

We need to convert the linear people structure into Parent-Child relation with unbounded depth using XQuery. To give in detail, we have the XML schema as

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/ParentChild" xmlns:tns="http://www.example.org/ParentChild" elementFormDefault="qualified">

<complexType name="People">
<sequence>
<element name="id" type="string" />
<element name="name" type="string" />
<element name="age" type="string" />
<element name="parentId" type="string" minOccurs="0" />
</sequence>
</complexType>

<complexType name="Peoples">
<sequence>
<element name="people" type="tns:People" maxOccurs="unbounded"></element>
</sequence>
</complexType>
<element name="Peoples" type="tns:Peoples"></element>
<element name="People" type="tns:People"></element>

<complexType name="Parent">
<sequence>
<element name="id" type="string" />
<element name="name" type="string" />
<element name="age" type="string" />
<element name="child" type="tns:Parent" minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>

<complexType name="Parents">
<sequence>
<element name="Parent" type="tns:Parent" maxOccurs="unbounded" ></element>
</sequence>
</complexType>

<element name="Parents" type="tns:Parents"></element>
<element name="Parent" type="tns:Parent"></element>

</schema>

The input structure can appear as
<?xml version="1.0"?>
<ns0:Peoples xmlns:ns0="http://www.example.org/ParentChild">
<ns0:people>
<ns0:id>1</ns0:id>
<ns0:name>x</ns0:name>
<ns0:age>11</ns0:age>
<ns0:parentId>2</ns0:parentId>
</ns0:people>
<ns0:people>
<ns0:id>2</ns0:id>
<ns0:name>y</ns0:name>
<ns0:age>11</ns0:age>
<ns0:parentId>3</ns0:parentId>
</ns0:people>
<ns0:people>
<ns0:id>3</ns0:id>
<ns0:name>z</ns0:name>
<ns0:age>11</ns0:age>
</ns0:people>
<ns0:people>
<ns0:id>5</ns0:id>
<ns0:name>a</ns0:name>
<ns0:age>11</ns0:age>
</ns0:people>
</ns0:Peoples>

The response should be like below
<?xml version="1.0"?>
<ns0:Parents xmlns:ns0="http://www.example.org/ParentChild">
<ns0:Parent>
<ns0:id>3</ns0:id>
<ns0:name>z</ns0:name>
<ns0:age>11</ns0:age>
<ns0:child>
<ns0:id>2</ns0:id>
<ns0:name>y</ns0:name>
<ns0:age>11</ns0:age>
<ns0:child>
<ns0:id>1</ns0:id>
<ns0:name>x</ns0:name>
<ns0:age>11</ns0:age>
</ns0:child>
</ns0:child>
</ns0:Parent>
<ns0:Parent>
<ns0:id>5</ns0:id>
<ns0:name>a</ns0:name>
<ns0:age>11</ns0:age>
</ns0:Parent>
</ns0:Parents>

We tried with below XQuery, but it is not resulting as expected.

(:: pragma bea:global-element-parameter parameter="$peoples" element="ns0:Peoples" location="ParentChild.xsd" ::)
(:: pragma bea:global-element-return element="ns0:Parents" location="ParentChild.xsd" ::)

declare namespace ns0 = "http://www.example.org/ParentChild";
declare namespace xf = "http://tempuri.org/RecursiveParentChild/ParentChild/";

declare function xf:ParentChild($peoples as element(ns0:Peoples))
as element(ns0:Parents) {
<ns0:Parents>
{
let $results := <a>{
for $people1 in $peoples/ns0:people where (not (exists ($people1/*:parentId)))
return
<ns0:Parent>
<ns0:id>{ data($people1/ns0:id) }</ns0:id>
<ns0:name>{ data($people1/ns0:name) }</ns0:name>
<ns0:age>{ data($people1/ns0:age) }</ns0:age>
</ns0:Parent>
}
</a>

let $result1 :=
for $people1 in $peoples/ns0:people where (exists ($people1/*:parentId))
return
if (data($people1/ns0:parentId) = data($results/ns0:id)) then
<ns0:child>
$people1/*
</ns0:child>
else()

return $result1
}
</ns0:Parents>
};

declare variable $peoples as element(ns0:Peoples) external;

xf:ParentChild($peoples)

Anyone tried similar kind of XQuery?. THere is a possibility that the child node can appear before the parent node, how we can handle that as well?

Any help on this is appreciated

Regards
Venkata Madhu

Answers

  • odie_63
    odie_63 Member Posts: 8,493 Silver Trophy
    You need a recursive function in this situation :
    declare namespace ns0 = "http://www.example.org/ParentChild";
    declare namespace xf = "http://tempuri.org/RecursiveParentChild/ParentChild/";
    declare variable $peoples as element(ns0:Peoples) external;
    
    declare function xf:getChildren($p as element(ns0:people)) as element(ns0:child)*
    {
      
      for $c in $peoples/ns0:people[ns0:parentId=$p/ns0:id]
      return <ns0:child>
    	   <ns0:id>{ data($p/ns0:id) }</ns0:id>
    	   <ns0:name>{ data($c/ns0:name) }</ns0:name>
    	   <ns0:age>{ data($p/ns0:age) }</ns0:age>
    	   { xf:getChildren($c) }
             </ns0:child>
    };
    
    <ns0:Parents>
    {
      for $p in $peoples/ns0:people[not(ns0:parentId)]
      return <ns0:Parent>
    	   <ns0:id>{ data($p/ns0:id) }</ns0:id>
    	   <ns0:name>{ data($p/ns0:name) }</ns0:name>
    	   <ns0:age>{ data($p/ns0:age) }</ns0:age>
    	   { xf:getChildren($p) }
             </ns0:Parent>
    }
    </ns0:Parents>
  • It is working as expected, thanks. Still can we modify to have only one single for loop?
This discussion has been closed.
developers