3

XML elements and JSON properties of the same name may be siblings. I.e.:

<container>
  <value>value1</value>
  <value>value2</value>
</container>

and

object-node {
  "value" : "value1",
  "value" : "value2"
}

are both valid, but I have not found a valid way to transform one into the other. It's invalid to dynamically build properties within an object-node constructor, i.e.:

object-node {
  for $v in $values
  return 'value' : $v
}

Using maps doesn't work because the duplicate key names are collapsed:

xdmp:to-json(map:new((
  map:entry("value", "value1"), 
  map:entry("value", "value2")))
  )

=> {"value":"value2"}

And when using json:object, the last key value is duplicated:

json:object(<json:object>
  <json:entry key="value">
    <json:value>value1</json:value>
  </json:entry>
  <json:entry key="value">
    <json:value>value2</json:value>
  </json:entry>
</json:object>)

=> {"value":"value2", "value":"value2"}

Joining maps using the + operator is better, but it merges the duplicate keys into a single key with an array of values ({"value":["value1", "value2"]}), which is still not what I want. Is there any way to build sibling JSON properties of the same name dynamically in XQuery?

wst
  • 11,681
  • 1
  • 24
  • 39
  • I'd concur with Michael Kay, and recommend avoiding non-unique property names. There are ways to make it work, but you'll get side-effects later on, like potential difficulty updating JSON docs with non-unique properties once persisted in a MarkLogic database. – grtjn Dec 03 '17 at 11:53
  • @grtjn Yes, I agree this seems to be the right thing to do. However, MarkLogic explicitly allows this behavior, while no one else seems to. Handling duplicate property names would have been convenient for keeping transformed JSON documents closer in structure to their XML equivalents, but I will work around it. – wst Dec 03 '17 at 15:30
  • MarkLogic only tolerates it because it doesn't like data getting lost, but that doesn't mean it doesn't mind. Like said, you can persist JSON with non-unique properties, but each time you touch the JSON you have to be careful how you handle it. And while `xdmp:unquote` allows parsing non-unique properties, `xdmp:from-json-string` doesnt, and will throw an error. – grtjn Dec 03 '17 at 16:05

2 Answers2

5

Your JSON example:

object-node {
  "value" : "value1",
  "value" : "value2"
}

isn't really valid: or at any rate, it's best avoided. RFC 7159 says:

When the names within an object are not unique, the behavior of software that receives such an object is unpredictable. Many implementations report the last name/value pair only. Other implementations report an error or fail to parse the object, and some implementations report all of the name/value pairs, including duplicates.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thanks Michael. My assumptions about what is valid JSON were based on what appears to be MarkLogic-specific behavior. Equivalent JSON constructors in FF, Safari, Chrome, and eXist-db all result in the same behavior: report the last key/value pair only (Safari throws an exception, in addition to creating the variable). – wst Dec 03 '17 at 15:31
2

I don't believe that it is possible to embed a FLWOR inside of the object-node() constructor.

You could construct a string and evaluate with xdmp:eval() or xdmp:value():

let $container := 
    <container>
      <value>value1</value>
      <value>value2</value>
    </container>

return
  xdmp:value(
   "object-node {" || 
     fn:string-join($container/value ! ('"' || local-name() || '": "' || . || '"'), ",") || 
   " }"
  )

Or build the JSON string and use xdmp:unquote():

let $container := 
    <container>
      <value>value1</value>
      <value>value2</value>
    </container>

return
  xdmp:unquote(
   "{" || 
     fn:string-join($container/value ! ('"' || local-name() ||'": "' || . || '"'), ",") || 
   "}"
  )
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • 1
    `xdmp:eval` is slow, `xdmp:value` would be quicker. And if you are generating a string anyhow, consider building a JSON string, and parsing it with `xdmp:unquote`. I still wouldn't recommend non-unique properties though.. :) – grtjn Dec 03 '17 at 16:07