0

I have the following xml document:

<customers>
   <continent>NA</continent>
   <Type>Regular<Type>
   <customer>
      <name>John Smith</name>
      <address>123 Oak St.</address>
      <state>WA</state>
      <phone>(206) 123-4567</phone>
   </customer>
   <customer>
      <name>Zack Zwyker</name>
      <address>368 Elm St.</address>
      <state>WA</state>
      <phone>(206) 423-4537</phone>
   </customer>
<customers>

I am trying to construct a map for each customer as follows:

for $customer in //customers
return 
map {
'continent': //continent/string(),
'type': //Type/string(),
'name': $customer/name/string(),
'address': $customer/address/string(),
'state': $customer/state/string(),
'phone': $customer/phone/string()
}

but for each iteration i am using the same continent and type which are same for each customer.

how can i construct my xpath so that the continent and type elements are generated only once, and can be accessed in each map.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
pavan
  • 334
  • 6
  • 20

2 Answers2

1

I think you intended for $customer in //customer (not //customers).

The Saxon-EE optimizer will automatically pull the expressions //continent/string() and //Type/string() out of the "for" loop, but if you want to do this optimization manually you can write:

let $continent := //continent/string(), $type := //Type/string()
return
  for $customer in //customers
  return 
    map {
     'continent': $continent,
     'type': $type,
     'name': $customer/name/string(),
     'address': $customer/address/string(),
     'state': $customer/state/string(),
     'phone': $customer/phone/string()
   }

Alternatively, I would probably write this without any variables as

(//customer) !
    map {
     'continent': string(..!continent),
     'type': string(..!type),
     'name': string(name),
     'address': string(address),
     'state': string(state),
     'phone': string(phone)
   }
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Dr. Kay, I think the OP wants to store a "reference" to the common values -- not the values themselves. I showed how to do this in the second variant of my answer – Dimitre Novatchev Dec 21 '19 at 04:14
0

but for each iteration i am using the same continent and type which are same for each customer.

how can i construct my xpath so that the continent and type elements are generated only once, and can be accessed in each map.

I would first create another map and assign it to a variable:

let $common := map
{
 'continent' : /customers/continent/string(.),
 'type' : /customers/Type/string(.)
}
 return
  for $customer in /customers/customer
  return 
    map {
     'continent': $common?continent,
     'type': $common?type,
     'name': $customer/name/string(),
     'address': $customer/address/string(),
     'state': $customer/state/string(),
     'phone': $customer/phone/string()
   } 

Or one can even just store $common in each map -- this means that whenever continent or type are needed from any map (let's say contained in a variable $theMap), these will be accessed as:

$theMap?common?continent

and

$theMap?common?type

If this design is preferrable to you, then the code to generate the maps will be:

let $common := map
{
 'continent' : /customers/continent/string(.),
 'type' : /customers/Type/string(.)
}
 return
  for $customer in /customers/customer
  return 
    map {
     'common': $common,
     'name': $customer/name/string(),
     'address': $customer/address/string(),
     'state': $customer/state/string(),
     'phone': $customer/phone/string()
   } 
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431