简体   繁体   中英

xpath expression to force to return absent nodes

<matrix id="2x2withNulls">
    <row>
        <column num="1">346</column>
    </row>
    <row>
        <column num="1">444</column>
        <column num="2">56</column>
    </row>
</matrix>

I need to iterate over the whole matrix, is there any way to force the expression to create a node where null?

Ideally /matrix/row/column someway returns the four expected nodes.

/matrix/row/column[as many as max(foreach /matrix/column/)] , duplicate if necessary or /matrix/row/column[not(exists num where exists in other colum)-> create it]

I can only use one expression, as the expression is a parameter for a third party application.

Perhaps there must a way to force to result column num1 repeated for the first row, so it always returns max(columns) even if they are duplicates of the last existintg column in the half empty row.

The concept of "selecting an absent node" is an interesting one, but is likely to lead you down a false trail. As some philosopher once said of God, existence is not a predicate; you can't select all the nodes that don't exist.

The tricky bit is your statement "I can only use one expression, as the expression is a parameter for a third party application." That seems to rule out XSLT, although you've included the XSLT tag in your question. So I think it would be good to know the constraints more precisely.

I would tweak Erat's solution a little:

return <matrix> {
  $matrix/@*,
  for $row in $matrix/row
  return <row> {
    for $i in 1 to 2
    return ($row/column[@num=$i], <column num="{$i}"/>)[1]
  } </row>
}</matrix>

If you are creating the xml yourself, you could start out by making sure it validates against an xsd that has a sequence with minOccurs and maxOccurs set to a desired count; that would do the trick in constraining the number of nodes per se.

Read up on xsd sequence here .

If you're getting this xml out of the 3rd-party app, but want to include empty nodes, you can use LinqToXml (assuming you can use C# in your project and also assuming xDoc is an XDocument containing the xml ):

public void AddEmptyNodesIf(XDocument xDoc, int targetElementCount = 4)
{
    XElement e = new XElement("column");
    e.Add(new XAttribute("num", null));

    foreach (XElement row in xDoc.Elements("row"))
    {
        int lastOccurringId = int.Parse(row.Elements("column")
            .Last().Attribute("num").Value);

        while (row.Elements("column").Count() < targetElementCount)
        {
            lastOccurringId++;
            e.Attribute("num").Value = lastOccurringId.ToString();
            row.Add(e);
        }
    }
}

Note that I did not test this, but even if it doesn't completely work, you get the general gist of it.

You will have to reconstruct the result, and create new columns when necessary.

let $matrix :=
  <matrix id="2x2withNulls">
    <row>
      <column num="1">346</column>
    </row>
    <row>
      <column num="1">444</column>
      <column num="2">56</column>
    </row>
  </matrix>

return element matrix {
  $matrix/@*,
  for $row in $matrix/row
  return element row {
    for $i in 1 to 2
    return
      if ($row/column[@num=$i])
      then $row/column[@num=$i]
      else element column { attribute num { $i }}
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM