How i set variable in XSL in for - arrays

Sry, when i don't i find answare, but i don't make correct question. I have APEX 4 and trying create report from multiarray select. For example data is:
Name1 Value1
Name1 Value2
Name1 Value3
Name1 Value4
Name2 Value5
Name2 Value6
Name2 Value7
and I need output in report:
-Name1
--Value1
--Value2
--Value3
--Value4
-Name2
--Value5
--Value6
--Value7
I need do it in XSL similiar as php:
$x_key= '';
foreach($arr as $key=>$value)
{
if ($x_key != $key) {
$x_key = $key;
echo "-$key\n";
}
echo "--$value\n";
}
My code possible is (but don't do it what I want):
<xsl:variable name="NAMEOLD" select="X" />
<xsl:for-each select="//ROWSET1_ROW[position()]">
<xsl:variable name="NAME" select="NAME_NUMBER" />
<xsl:choose>
<xsl:when test="NAME!=NAMEOLD">
<xsl:variable name="NAMEOLD" select="NAME" />
<fo:table-row>
<fo:table-cell xsl:use-attribute-sets="cell-transparent" >
<fo:block xsl:use-attribute-sets="align-left txt" keep-together.within-page="always">
<xsl:value-of select="NAME_NUMBER"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:when>
</xsl:choose>
<fo:table-row>
<fo:table-cell xsl:use-attribute-sets="cell-transparent" >
<fo:block xsl:use-attribute-sets="align-left txt" keep-together.within-page="always">
<xsl:value-of select="VALUE"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
Thanks for any clue or answer
Stoupa101

You haven't shown your input XML. The simplest solution is along the following lines (note this requires XSLT 2.0):
<xsl:for-each-group select="ROW" group-adjacent="name">
<h1><xsl:value-of select="current-grouping-key"></h1>
<xsl:for-each select="current-group()"/>
<p><xsl:value-of select="value"/></p>
</xsl:for-each-group>
Don't try to do it the PHP way - there's no point writing low-level code when you have a high-level language.

Related

How to create 10 copies of the XML file, each with a different value of the td tag- PowerShell script

I just started learning PowerShell (literally - just a week and completely from tutorials and materials online :)) so don`t be harsh on me)
I would appreciate help and how to do it because I am stuck at this point.. So I have this template file, I created 10 copies of it but I have no idea how to change both td tag values "." with different name for Title and Artist in each copy.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<tr>
<td>.</td>
<td>.</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
This is how far I got but I would appreciate some help/advice/explanation for correct further steps..
[xml]$File= get-content "\MyXMLFile.xml"
1..10 |% { Copy-Item "\MyXMLFile.xml" "MyXMLFile$_.xml"}
$copiedFiles =#(Get-ChildItem -Path "XmlFileFolder" | % Name)
$exampleNode = $File.SelectNodes("//td[1]")
$TitleNodeArray = #('aa', 'bb', 'cc', etc..) #but I have no idea how to go on from there ..
Try this.
using assembly System
using assembly System.Linq
using assembly System.Xml.Linq
$inputFilename = "c:\temp\test.xml"
$xDoc = [System.Xml.Linq.XDocument]::Load($inputFilename)
$tds = $xDoc.Descendants("td").Foreach([System.Xml.Linq.XElement])
$td0 = $tds[0]
$td1 = $tds[1]
Write-Host $xDoc
$newWords1 = #("abc1","def1","ghi1","jkl1","mno1","pqr1","stu1","vwx1","yz11","2341")
$newWords2 = #("abc2","def2","ghi2","jkl2","mno2","pqr2","stu2","vwx2","yz12","2342")
for($i = 0; $i -lt 10; $i++)
{
Write-Host $i $newWords1[$i]
$td0.SetValue($newWords1[$i])
$td1.SetValue($newWords2[$i])
$xDoc.Save("c:\temp\testoutput" + $i + ".xml")
}
hmm, I don't work much with xml but I know of one-way which uses Select-Xml.
(Select-Xml -Xml $File -XPath '//tr/td')[1].Node."#text" = 'title'
Where [1] is the indexed node, and #text being the value/text itself.

What is a better way to write this comparative PowerShell script?

I'm new to PowerShell and I'm sure I'm not using best practices here. I've been working on this PowerShell script to compare two XML files. I start out by looping through each of the XML files and throwing the data into PS objects:
Here are some samples of the XML data:
XML file 1
<RESULTS>
<ROW>
<COLUMN NAME="ATTR1"><![CDATA[123456ABCDEF]]></COLUMN>
<COLUMN NAME="ATTR2"><![CDATA[1.0.4.0]]></COLUMN>
<COLUMN NAME="ATTR3"><![CDATA[Google.com]]></COLUMN>
<COLUMN NAME="ATTR4"><![CDATA[Lorem ipsum]]></COLUMN>
<COLUMN NAME="ATTR5"><![CDATA[This is some text]]></COLUMN>
</ROW>
<ROW>
<COLUMN NAME="ATTR1"><![CDATA[123456ABCDEF]]></COLUMN>
<COLUMN NAME="ATTR2"><![CDATA[2.0.0.1]]></COLUMN>
<COLUMN NAME="ATTR3"><![CDATA[HelloWorld.com]]></COLUMN>
<COLUMN NAME="ATTR4"><![CDATA[Lorem ipsum]]></COLUMN>
<COLUMN NAME="ATTR5"><![CDATA[This is some text]]></COLUMN>
</ROW>
<ROW>
<COLUMN NAME="ATTR1"><![CDATA[123456ABCDEF]]></COLUMN>
<COLUMN NAME="ATTR2"><![CDATA[5.6.7.0]]></COLUMN>
<COLUMN NAME="ATTR3"><![CDATA[foo_foo_6 (2).org]]></COLUMN>
<COLUMN NAME="ATTR4"><![CDATA[Lorem ipsum]]></COLUMN>
<COLUMN NAME="ATTR5"><![CDATA[This is some text]]></COLUMN>
</ROW>
</RESULTS>
XML File 2
<applications xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<application>
<name>Google.com</name>
<version>1.2.0.0</version>
</application>
<application>
<name>HelloWorld.com</name>
<version>2.0.0.1</version>
</application>
<application>
<name>FOO_FOO.org</name>
<version>6.2.0.1</version>
</application>
</applications>
Creating arrays/objects filled with XML data
# assign all output from `foreach` loop to `$array1` - XML file 1
$array1 = foreach($row in $xmldata1.RESULTS.ROW){
# create new object with the pertinent details as property values
[pscustomobject]#{
Name = $row.COLUMN.Where{ $_.NAME -eq "ATTR3"}.'#cdata-section'
Version = $row.COLUMN.Where{ $_.NAME -eq "ATTR2"}.'#cdata-section'
}
}
# assign all output from `foreach` loop to `$array2` - XML file 2
$array2 = foreach($row in $xmldata2.applications.application){
# create new object with the pertinent details as property values
[pscustomobject]#{
Name = $row.name
Version = $row.version
}
}
This is the script I'm wondering how to write more effectively. It simply loops through $array1 and compares it with the data in $array2. If there is a match in the name, and a mismatch in the version, then it will store those values in a PS object.
Script I want to improve
#loop through array 1
for($i = 0; $i -le $array1.Length; $i++)
{
#loop through array 2
for($j = 0; $j -le $array2.Length; $j++)
{
#if file name in array 1 matches a name in array 2...
if (($array1.name[$i] -eq $array2.name[$j]) -or ($array1.name[$i].Substring(0, [Math]::Min($array1.name[$i].Length, 7)) -eq $array2.name[$j].Substring(0, [Math]::Min($array2.name[$i].Length, 7))))
{
#then, if that file names version does not match the version found in array 2...
if($array1.version[$i] -ne $array2.version[$j])
{
#create new object
[pscustomobject]#{
Name = $array1.name[$i]
Name2 = $array2.name[$j]
Version = $array1.version[$i]
Version2 = $array2.version[$j]
}
}
}
}
}
However, there are some names that don't match perfectly. So I use the -or operator and throw this line in my first if-statement to compare the first 7 characters of the file name in each array to see if there's some kind of match (which, I know there are):
($array1.name[$i].Substring(0, [Math]::Min($array1.name[$i].Length, 7)) -eq $array2.name[$j].Substring(0, [Math]::Min($array2.name[$i].Length, 7)))
Whenever I add that line though I get the following error for only some of the data objects in the arrays. The script will return some objects, but most of the time my console pane will be filled with the following error:
Error
You cannot call a method on a null-valued expression.
At line:8 char:13
+ if (($array1.name[$i] -eq $array2.name[$j]) -or ($array1 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
I don't even know what it's talking about. Cause when I extract that line and put actual indices in it, it works fine.
Example
if($array1.name[1020].Substring(0, [Math]::Min($array1.name[1020].Length, 7)) -eq $array2.name[2500].Substring(0, [Math]::Min($array2.name[2500].Length, 7))){
So, I'm stumped. Is there a better way to compare these two arrays and get a similar output?
I believe this could work and might be a more direct way to do it, this method would not require you to do the object construction of the first XML. Hopefully the inline comments explains the logic.
:outer foreach($i in $xml1.results.row) {
$name = $i.Column.Where{ $_.NAME -eq 'ATTR3' }.'#cdata-section'
$version = $i.Column.Where{ $_.NAME -eq 'ATTR2' }.'#cdata-section'
foreach($z in $xml2.applications.application) {
# check if they have the same version
$sameVersion = $version -eq $z.Version
# check if they have the same name
$sameName = $name -eq $z.Name
# if both conditions are `$true` we can skip this and continue with
# next item of outer loop
if($sameVersion -and $sameName) {
continue outer
}
# if their first 7 characters are the same but they're NOT the same version
if([string]::new($name[0..6]) -eq [string]::new($z.Name[0..6]) -and -not $sameVersion) {
[pscustomobject]#{
Name = $name
Name2 = $z.Name
Version = $version
Version2 = $z.Version
}
}
}
}
The result of this would be:
Name Name2 Version Version2
---- ----- ------- --------
Google.com Google.com 1.0.4.0 1.2.0.0
foo_foo_6 (2).org FOO_FOO.org 5.6.7.0 6.2.0.1
See Using a labeled continue in a loop which describes and explains the use of continue outer in this example.

How to keep whitespace formatting while reading a file into an array using perl?

I am trying to read a text file into an array of lines while keeping the whitespace formatting of each line. Once I read the entire file into an array, if I iterate through the array printing all of the lines to STDOUT, all of the whitespace formatting is lost, as well as some of the text (look at the first line). But, if I print the lines while reading the file, and printing the array using Data::Dumper, the whitespace formatting is still there.
Since I am trying to capture the output on STDOUT while iterating through the array, please tell me what I need to do to print out the properly whitespace formatted text.
The code and output are pretty much self explanatory, as can be seen below.
I dual boot between macOS Mojave 10.14.6 and Linux Mint 20 and have the same problem on both systems with different versions of perl, versions below.
Thanks in advance for any advice!!!
macOS:
macOS Mojave 10.14.6
Darwin MBPro.lan 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64 x86_64
This is perl 5, version 18, subversion 4 (v5.18.4) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)
linux:
Linux mintMBP 5.4.0-47-generic #51-Ubuntu SMP Fri Sep 4 19:50:52 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
This is perl 5, version 30, subversion 0 (v5.30.0) built for x86_64-linux-gnu-thread-multi
(with 46 registered patches, see perl -V for more detail)
Here is the code:
#!/usr/bin/perl
use Data::Dumper;
my $filename = "formatted.txt";
my #lines;
open(my $FH, "<", $filename)
or die "Can't open < $filename: $!";
print "\nText printed while reading file:\n\n";
while(<$FH>) {
chomp($_);
push(#lines, $_);
print $_ . "\n";
}
close($FH);
print "\nText printed while iterating the line array:\n\n";
while(<#lines>) {
print $_ . "\n";
}
print "\nText printed using Data::Dumper :\n\n";
print Dumper(#lines);
Here is the whitespace formatted input file:
<?xml version="1.0" encoding="UTF-8"?>
<dict>
<key>Definitions</key>
<dict>
<key>src/main.c</key>
<dict>
<key>Path</key>
<string>src/main.c</string>
<key>Group</key>
<array>
<string>src</string>
</array>
</dict>
</dict>
</dict>
Here is the output:
[MBPro:Development/tmp/perl_formatted_lines] justin% ./file_test.pl
Text printed while reading file:
<?xml version="1.0" encoding="UTF-8"?>
<dict>
<key>Definitions</key>
<dict>
<key>src/main.c</key>
<dict>
<key>Path</key>
<string>src/main.c</string>
<key>Group</key>
<array>
<string>src</string>
</array>
</dict>
</dict>
</dict>
Text printed while iterating the line array:
version=1.0
<dict>
<key>Definitions</key>
<dict>
<key>src/main.c</key>
<dict>
<key>Path</key>
<string>src/main.c</string>
<key>Group</key>
<array>
<string>src</string>
</array>
</dict>
</dict>
</dict>
Text printed using Data::Dumper :
$VAR1 = '<?xml version="1.0" encoding="UTF-8"?>';
$VAR2 = '<dict>';
$VAR3 = ' <key>Definitions</key>';
$VAR4 = ' <dict>';
$VAR5 = ' <key>src/main.c</key>';
$VAR6 = ' <dict>';
$VAR7 = ' <key>Path</key>';
$VAR8 = ' <string>src/main.c</string>';
$VAR9 = ' <key>Group</key>';
$VAR10 = ' <array>';
$VAR11 = ' <string>src</string>';
$VAR12 = ' </array>';
$VAR13 = ' </dict>';
$VAR14 = ' </dict>';
$VAR15 = '</dict>';
Don't use while (<#lines>), it doesn't do what you think.*
Instead, use a for loop:
for (#lines) {
print $_, "\n";
}
*) It calls glob, i.e. it joins the contents of the array with $" and interprets the result as a glob expression, i.e. whitespace separated wildcard patterns.
Corrected OP's demo code to read lines into an array #lines
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my $fname = 'formatted.txt';
my #lines;
open my $fh, '<', $fname
or die "Couldn't open $fname";
#lines = <$fh>;
close $fh;
chomp(#lines);
say Dumper(\#lines);
say for #lines;
Input
<?xml version="1.0" encoding="UTF-8"?>
<dict>
<key>Definitions</key>
<dict>
<key>src/main.c</key>
<dict>
<key>Path</key>
<string>src/main.c</string>
<key>Group</key>
<array>
<string>src</string>
</array>
</dict>
</dict>
</dict>
Output
$VAR1 = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<dict>',
' <key>Definitions</key>',
' <dict>',
' <key>src/main.c</key>',
' <dict>',
' <key>Path</key>',
' <string>src/main.c</string>',
' <key>Group</key>',
' <array>',
' <string>src</string>',
' </array>',
' </dict>',
' </dict>',
'</dict>'
];
<?xml version="1.0" encoding="UTF-8"?>
<dict>
<key>Definitions</key>
<dict>
<key>src/main.c</key>
<dict>
<key>Path</key>
<string>src/main.c</string>
<key>Group</key>
<array>
<string>src</string>
</array>
</dict>
</dict>
</dict>

How to use XQuery to simulate STRING_AGG() (grouped string concatenation)?

I have XML data which I need to convert to relational form. I use XQuery cause I don't know the number of address nodes. I'd like to get the whole address/adresses separeted by a comma. I guess I need to use LET clause but I'm still receiving an error.
Here's my code:
declare #xml as xml = '<root>
<Row>
<proceeding>
<signatures>V GU 86/18</signatures>
<signatures>V GUp 9/19</signatures>
<signatures>V GUp 8/19</signatures>
</proceeding>
<entity>
<info>
<cleaned_name>Kate Smith</cleaned_name>
</info>
<address>
<town>London </town>
<house_number>1 </house_number>
<flat_number>1</flat_number>
<street>Downing Street</street>
<zip_code>00-001</zip_code>
</address>
</entity>
<entity>
<info>
<cleaned_name>John Smith</cleaned_name>
</info>
<address>
<town>Washington </town>
<house_number>1</house_number>
<flat_number>1</flat_number>
<street>Pennsylvania Avenue</street>
<zip_code>00-001</zip_code>
</address>
</entity>
</Row>
</root>'
select
isnull(STUFF(a.x.query('for $s in entity/info/cleaned_name return <x>{concat(",",$s)}</x>').value('.','varchar(max)'),1,1,''),'') as 'Nazwa podmiotu'
,isnull(STUFF(a.x.query('for $s in proceeding/signatures return <x>{concat(",",$s)}</x>').value('.','varchar(max)'),1,1,''),'') as 'Sygnatura'
--,isnull(STUFF(a.x.query('for $s in entity let $L := $s/entity/address return <x>{concat(",",Address="{$s/Address}")}</x>').value('.','varchar(max)'),1,1,''),'')
from #xml.nodes('/root/Row') as a(x)
My desired outcome
Are you looking for this?
select
isnull(STUFF(a.x.query('for $s in entity/info/cleaned_name return <x>{concat(",",$s)}</x>').value('.','varchar(max)'),1,1,''),'') as 'Nazwa podmiotu'
,isnull(STUFF(a.x.query('for $s in proceeding/signatures return <x>{concat(",",$s)}</x>').value('.','varchar(max)'),1,1,''),'') as 'Sygnatura'
,isnull(STUFF(a.x.query('for $s in entity
return <x>
{
concat(", ",($s/address/zip_code/text())[1]," "
,($s/address/town/text())[1]," "
,($s/address/street/text())[1]," "
,($s/address/house_number/text())[1],"/"
,($s/address/flat_number/text())[1]
)
}
</x>').value('.','varchar(max)'),1,2,''),'')
from #xml.nodes('/root/Row') as a(x);
The result
Nazwa podmiotu Sygnatura AllAdresses
Kate Smith,John Smith V GU 86/18,V GUp 9/19,V GUp 8/19 00-001 London Downing Street 1 /1, 00-001 Washington Pennsylvania Avenue 1/1
UPDATE Multiple addresses and identical data
You can try this (according to your comment)
Your test data with one second address and one copied address:
declare #xml as xml = '<root>
<Row>
<proceeding>
<signatures>V GU 86/18</signatures>
<signatures>V GUp 9/19</signatures>
<signatures>V GUp 8/19</signatures>
</proceeding>
<entity>
<info>
<cleaned_name>Kate Smith</cleaned_name>
</info>
<address>
<town>London </town>
<house_number>1 </house_number>
<flat_number>1</flat_number>
<street>Downing Street</street>
<zip_code>00-001</zip_code>
</address>
<address>
<town>Yorkshire </town>
<house_number>1 </house_number>
<flat_number>1</flat_number>
<street>Morning Street</street>
<zip_code>00-999</zip_code>
</address>
</entity>
<entity>
<info>
<cleaned_name>John Smith</cleaned_name>
</info>
<address>
<town>Washington </town>
<house_number>1</house_number>
<flat_number>1</flat_number>
<street>Pennsylvania Avenue</street>
<zip_code>00-001</zip_code>
</address>
<address>
<town>Washington </town>
<house_number>1</house_number>
<flat_number>1</flat_number>
<street>Pennsylvania Avenue</street>
<zip_code>00-001</zip_code>
</address>
</entity>
</Row>
</root>'
--The query
select
isnull(STUFF(a.x.query('for $s in entity/info/cleaned_name return <x>{concat(",",$s)}</x>').value('.','varchar(max)'),1,1,''),'') as 'Nazwa podmiotu'
,isnull(STUFF(a.x.query('for $s in proceeding/signatures return <x>{concat(",",$s)}</x>').value('.','varchar(max)'),1,1,''),'') as 'Sygnatura'
,isnull(STUFF(a.x.query('for $s in entity/address
return
<x>{concat(", ",($s/zip_code/text())[1]," "
,($s/town/text())[1]," "
,($s/street/text())[1]," "
,($s/house_number/text())[1],"/"
,($s/flat_number/text())[1]
)}</x>')
.query('for $a in distinct-values(/x/text()) return $a').value('.','varchar(max)'),1,2,''),'')
from #xml.nodes('/root/Row') as a(x);
The idea in short:
We use the first XQuery to create a simple XML fragment like this
<x>, 00-001 London Downing Street 1 /1</x>
<x>, 00-999 Yorkshire Morning Street 1 /1</x>
<x>, 00-001 Washington Pennsylvania Avenue 1/1</x>
<x>, 00-001 Washington Pennsylvania Avenue 1/1</x>
With this we can use a second XQuery and place distinct-values() there.

bash to parse XML into multidimensional array

I have a XML file which I want to parse into bash variables/arrays.
I have limited linux commands (busybox) available since I am working on a NAS box.
My XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?> .
<WEBCAMS>
<CAM>
<DESCRIPTION>description for cam 1</DESCRIPTION>
<URL>http://myURLtoWebcam1/cam1/pic.jpg</URL>
<FILENAME>filename1</FILENAME>
</CAM>
<CAM>
<DESCRIPTION>description for cam 2</DESCRIPTION>
<URL>http://myURLtoWebcam2/cam2/pic.jpg</URL>
<FILENAME>filename2</FILENAME>
</CAM>
</WEBCAMS>
my bash script so far:
#!/bin/sh
rdom () { local IFS=\> ; read -d \< E C ;}
while rdom; do
if [[ $E = DESCRIPTION ]]; then
counter=$((counter+1))
declare cam$counter="$C"
fi
done < webcams.xml
I want to get the XML content like the following:
echo "Cam1 description: ${cam1[0]}"; ## should show: description for cam 1
echo "Cam1 URL: ${cam1[1]}"; ## should show: http://myURLtoWebcam1/cam1/pic.jpg
echo "Cam1 filename: ${cam1[2]}"; should show: filename1
echo "Cam2 description: ${cam2[0]}"; ## should show: description for cam 2
echo "Cam2 URL: ${cam2[1]}"; ## should show: http://myURLtoWebcam1/cam2/pic.jpg
echo "Cam2 filename: ${cam2[2]}"; ## should show: filename2
So far I am only able to read the "DESCRIPTION" fields into bash variables.
Any idea how to get the other fields "URL" and "FILENAME" into my arrays/variables? The so far found solutions did not fit or could not be modified to my needs due to the limited Linux commands on my NAS.
If XSLTPROC is available you can use it - bonus its a real XML parser.
> xsltproc transform.xsl webcams.xml
Cam1 description: description for cam 1
Cam1 URL: http://myURLtoWebcam1/cam1/pic.jpg
Cam1 filename: filename1
Cam2 description: description for cam 2
Cam2 URL: http://myURLtoWebcam2/cam2/pic.jpg
Cam2 filename: filename2
Where transform.xsl is
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="CAM">
<xsl:variable name="i" select="position()" />
Cam<xsl:value-of select="$i"/> description: <xsl:value-of select="DESCRIPTION"/>
Cam<xsl:value-of select="$i"/> URL: <xsl:value-of select="URL"/>
Cam<xsl:value-of select="$i"/> filename: <xsl:value-of select="FILENAME"/>
</xsl:template>
<xsl:template match="/WEBCAMS"><xsl:apply-templates select="*"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
This seems to work ... but still the 1,5D dimensional array or the way to escape variables makes me headache - see below problems within a FOR loop:
#!/bin/sh
rdom () { local IFS=\> ; read -d \< E C ;}
while rdom; do
if [[ $E = DESCRIPTION ]]; then
counter0=$((counter0+1))
declare -a cam$((counter0))[0]="$C"
fi
if [[ $E = URL ]]; then
counter1=$((counter1+1))
declare -a cam$((counter1))[1]="$C"
fi
if [[ $E = FILENAME ]]; then
counter2=$((counter2+1))
declare -a cam$((counter2))[2]="$C"
fi
done < webcams.xml
echo "Cam1 description: ${cam1[0]}";
echo "Cam1 URL: ${cam1[1]}";
echo "Cam1 filename: ${cam1[2]}";
echo "Cam2 description: ${cam2[0]}";
echo "Cam2 URL: ${cam2[1]}";
echo "Cam2 filename: ${cam2[2]}";
But it is still impossible to get the values within a FOR loop like:
for (( c=1; c<=$counter0; c++ ))
do
var=cam$c;
echo "Cam$c description: ${!var[0]}";
echo "Cam$c URL: ${!var[1]}";
echo "Cam$c filename: ${!var[2]}";
done

Resources