2

First time poster here, so please don't hesitate to let me know if I've done something wrong.

I'm using Altova Stylevision to print out form documents, and I need to print a copy of the form for each unique set of two node collections. Below is a sample XML to better explain what I'm asking for.

<form>
    <Animals>
        <Animal>
            <AName>Boomer</AName>
            <AAddress>House</AAddress>
        </Animal>
        <Animal>
            <AName>Growl</AName>
            <AAddress>Street</AAddress>
        </Animal>
        <Animal>
            <AName>Incognito</AName>
            <AAddress>Nowhere</AAddress>
        </Animal>
    </Animals>
    <People>
        <Person>
            <PName>Willy</PName>
            <PAddress>123 Common Lane</PAddress>
        </Person>
        <Person>
            <PName>Wonka</PName>
            <PAddress>Chocolate Factory</PAddress>
        </Person>
    </People>
</form>

I'd like to produce 6 unique values (the Cartesian Product) from the above sample, containing every combination of <Animal> and <Person> where neither of them are null. So, the output for this example would be like (just showing the grouping, need all child nodes instead of just names):

([Boomer, Willy], [Boomer, Wonka], [Growl, Willy], [Growl, Wonka], [Incognito, Willy], [Incognito, Wonka])

EDIT: I'm trying to get the nodes, not just the <Name> element value. Because I'm wrapping the whole StyleVision form in this template, the scope should be at the <Animal> and <Person> level. This way, in the form I can just say /AName, /PName, and get one value for /AName and /PName on each copy of the form. It should act as 6 instances of an element node, each one representing a unique combination of both <Person> and <Animal>.

I looked into the for # in # return syntax but I cannot figure out how to return the unique set. Below is what I've tried so far:

for $a in form/Animals/Animal, $p in form/People/Person return ($a, $p)

and

form/Animals/Animal | form/People/Person

Can anyone point me in the right direction?

Thanks!

semaj1919
  • 87
  • 7

1 Answers1

1

Update

Your for expression is correct, you just need to add an element constructor to get the result you need:

for $animal in $x/Animals/Animal,
    $person in $x/People/Person
return element union { $animal, $person }

The element name (union in the example) can be anything that you want.


These original code samples will generate the result strings from the question.

It's a little hard to read, but this will do what you want:

concat("(",
  concat(string-join(
    for $animal in /form/Animals/Animal/Name,
        $person in /form/People/Person/Name
    return concat("[", concat(string-join(($animal, $person), ", "), "]")), ", "), ")"))

If you can use an XPath/XQuery 3.0 processor, you can use the || concatenation operator instead of the concat function calls, which is (somewhat) more readable:

"(" ||
  string-join(
    for $animal in /form/Animals/Animal/Name,
        $person in /form/People/Person/Name
    return "[" || string-join(($animal, $person), ", ") || "]"
    , ", ")
|| ")"
joemfb
  • 3,056
  • 20
  • 19
  • Thanks for your answer. I realize now that my question was poorly formed and I didn't really specify what I was looking for. Instead of just string values, I'd like to take whole nodes. I just remembered it's called **Cartesian Product**, and I'd like the product of the nodes to represent one overall product set. – semaj1919 Dec 19 '13 at 19:29
  • Do you want the existing Animal and Person elements in a sequence, or do you want each union of an Animal and a Person inside of a new element? – joemfb Dec 20 '13 at 20:08
  • The latter, each union of an Animal and a Person inside of a new element. Your help is greatly appreciated! – semaj1919 Dec 23 '13 at 14:32