Skip to main content

XPath expression to select children of parent in xpath (that works in javascript and in c#)

TLDR: I want to write an xpath expression to select all (at any level) children of parent node using both '..' and 'parent::', that will work in c# and javascript/typescript.

I am limited to xsd 1.0. I do not want to use preceding-sibling axis.

My xml looks like

    <tns:s03.q01 id="s03.q01" text="Select the specific issues encountered (please select all that apply) and indicate the corresponding MBAs" description="" isAnsewerRequired="true" type="Question">
      <tns:choices>
        <tns:multiChoiceAssert condition="count(parent::tns:choices/*[@value='true']) = 0" message="'{$locator}\Choices' must have atleast one choice selected." relevantNodeLocator="string(../../@id)" />
        <tns:s03.q01.c01 value="true" id="s03.q01.c01" text="Bias" description="" type="Choice">
          <tns:selectMBAAssert condition="parent::*[@value='true'] and count(parent::*/tns:mba) = 0" message="'Select MBAs for {$locator}'." relevantNodeLocator="string(../@id)" />
          <tns:selectValueAssert condition="parent::*[@value='false'] and count(parent::*/tns:mba) &gt; 0" message="Select '{$locator}'." relevantNodeLocator="string(../@id)" />
          <tns:mba>m1</tns:mba>
          <tns:mba>m2</tns:mba>
        </tns:s03.q01.c01>
        <tns:s03.q01.c02 id="s03.q01.c02" text="Poor measurement quality" description="" type="Choice" value="false">
          <tns:selectMBAAssert condition="parent::*[@value='true'] and count(parent::*/tns:mba) = 0" message="'Select MBAs for {$locator}'." relevantNodeLocator="string(../@id)" />
          <tns:selectValueAssert condition="parent::*[@value='false'] and count(parent::*/tns:mba) &gt; 0" message="Select '{$locator}'." relevantNodeLocator="string(../@id)" />
        </tns:s03.q01.c02>
        <tns:s03.q01.c03 id="s03.q01.c03" text="Statistically significant MUF" description="" type="Choice" value="false">
          <tns:selectMBAAssert condition="parent::*[@value='true'] and count(parent::*/tns:mba) = 0" message="'Select MBAs for {$locator}'." relevantNodeLocator="string(../@id)" />
          <tns:selectValueAssert condition="parent::*[@value='false'] and count(parent::*/tns:mba) &gt; 0" message="Select '{$locator}'." relevantNodeLocator="string(../@id)" />
        </tns:s03.q01.c03>
      </tns:choices>
      <tns:comment id="s03.q01.comment" title="Comments" description="If the answer to the previous questions is “Yes,” please provide the details, including the document type (initial DIQ, DIQ update, initial LOF information, LOF information update), facility/MBA code, the date due and the date received.">
        <tns:s03.q01.comment dateRequested="2023-03-23" documentType="2" facility-mbaCode="m1" />
        <tns:s03.q01.comment dateRequested="2023-04-24" documentType="4" facility-mbaCode="f2" />
      </tns:comment>
    </tns:s03.q01>

I am using c# and javascript and hence I am limited to xsd 1.0 I define validations using nodes like selectMBAAssert and selectValueAssert with rule in the 'condition' attribute. So while parsing the xml I select all nodes with @condition attribute and evaluate the condition in the context of the same node.

The code to do so works fine in c# which looks like

            XmlDocument document = getDocumentWithSchema(xmlTextReader, schemaPath);//this is a method to load document with schema and xml.
            ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);

            var nsmgr = new XmlNamespaceManager(document.NameTable);
            nsmgr.AddNamespace("tns", "http://tempuri.org/XMLSchema2.xsd");

            document.Validate(eventHandler);
            //var nav = document.CreateNavigator();
            var list = document.SelectNodes("//*[@condition]", nsmgr);
            foreach (XmlNode node in list)
            {
                var condition = node.Attributes["condition"].Value;
                var nodeNav = node.CreateNavigator();
                var result = nodeNav.evaluateXPath(condition, nsmgr);
                //If the condition is "//tns:choices" results contains 1 element.
                if ((bool)result)
                {
                    var errorMsg = node.Attributes["message"].Value;
                    var locator = node.Attributes["relevantNodeLocator"].Value;

                    var nodeLocator = nodeNav.evaluateXPath(locator, nsmgr);

                    errorMsg = errorMsg.Replace("{$locator}", nodeLocator.ToString());
                    Console.WriteLine($"Validation error:{errorMsg}");
                }

            }

However code in typescript does work the same way.

  validateXML(questionId: any, xmlData: Document) {


    var conditionNodes = xmlData.evaluate("//*[@id='" + questionId + "']/*[@condition]", xmlData.documentElement, null, XPathResult.ANY_TYPE, null);
    var toretErrors: string = "";
    try {
      var currnetConditionNode: any = conditionNodes.iterateNext();

      while (currnetConditionNode) {
        //console.log(thisNode.textContent);

        var atributeValue = currnetConditionNode.attributes["condition"].nodeValue;
        var validationResult = xmlData.evaluate(atributeValue, currnetConditionNode, this.NSResolver, XPathResult.ANY_TYPE, null)
        console.log(validationResult);
        if (validationResult.booleanValue) {
          toretErrors += currnetConditionNode.attributes["message"].nodeValue;
        }
        currnetConditionNode = conditionNodes.iterateNext();
      }
    }
    catch (e) {
      console.error('Error: Document parsing error ' + e);
    }
    return toretErrors;

  }
  NSResolver(nsPrefix) {
    if (nsPrefix == "tns") {
      return "http://tempuri.org/XMLSchema2.xsd";
    }
    return null;
  }

How may i write the condition in selectMBAAssert node to select all (at any level) parent's children of type tns:mba?

Another question I think relevant to this question, are '..' and 'parent::' interchangeable? How may I script the condition attribute use both of them?

Sorry to make it lengthy question. Please let me know if you need any more details.

Via Active questions tagged javascript - Stack Overflow https://ift.tt/E1W2CjQ

Comments