-2

I have below xml string. i want to use js Regex to query the closest '<and>' and replace it with something.

<Query><Where><And><In><FieldRef Name='Title' /><Value Type='Text'>kk</Value></In><And><In><FieldRef Name='DocIcon' /><Value Type='Computed'>bb</Value></In><And><In><FieldRef Name='Continent' /><Value Type='Choice'>Europe</Value></In><Eq><FieldRef Name='Constituency' /><Value Type='Choice'>Jordan</Value></Eq></And></And></And></Where></Query>

Formatted:

<Query>
   <Where>
      <And>
         <In>
            <FieldRef Name='Title' />
            <Value Type='Text'>kk</Value>
         </In>
         <And>
            <In>
               <FieldRef Name='DocIcon' />
               <Value Type='Computed'>bb</Value>
            </In>
            <And>
               <In>
                  <FieldRef Name='Continent' />
                  <Value Type='Choice'>Europe</Value>
               </In>
               <Eq>
                  <FieldRef Name='Constituency' />
                  <Value Type='Choice'>Jordan</Value>
               </Eq>
            </And>
         </And>
      </And>
   </Where>
</Query>

enter image description here

<Eq><FieldRef Name="Constituency" /><Value Type="Choice">Jordan</Value></Eq> is a fixed string, other parts are not. which means there maybe many <and> <In>. How to get the closet <and>?

I tried below regex but fail:

/(<And>)<In>.*<\/In>(<Eq><FieldRef Name="Constituency" \/><Value Type="Choice">Jordan<\/Value><\/Eq>)<\/And>/gi

enter image description here

萧逸清
  • 165
  • 3
  • 11
  • 4
    And this is the time again to link to [this famous answer](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454). Don't use regex for this. – trincot Feb 15 '22 at 13:10
  • You need either XPath or settle for jQuery's [`.closest()`](https://api.jquery.com/closest/) method. There is practically zero reason to do this with regex unless you're not actually programming in JS and are hobbled by the UI of some system. – MonkeyZeus Feb 15 '22 at 13:33

1 Answers1

0

This is better done by parsing XML.

See:

DOMParser and filter

For example to get all nodes inside your XML that have the tag-name And use the JavaScript DOMParser:

xml = "<Query><Where><And><In><FieldRef Name='Title' /><Value Type='Text'>kk</Value></In><And><In><FieldRef Name='DocIcon' /><Value Type='Computed'>bb</Value></In><And><In><FieldRef Name='Continent' /><Value Type='Choice'>Europe</Value></In><Eq><FieldRef Name='Constituency' /><Value Type='Choice'>Jordan</Value></Eq></And></And></And></Where></Query>"

const parser = new DOMParser();
let xmlDoc = parser.parseFromString(xml,"text/xml");

const andElements = xmlDoc.getElementsByTagName("And");
console.log("<And> elements found: ", andElements.length);

function containsEqFieldRef(e) {
     return e.childNodes[1].tagName == 'Eq' && e.childNodes[1].childNodes[0].tagName == 'FieldRef';
}
let found = Array.from(andElements).filter(containsEqFieldRef);
console.log("found <And> with ./Eq/FieldRef:", found);
Now you need to make the node-position for Eq variable.

For the JS DOM API related functions and classes:

XPath

as suggested by MonkeyZeus XPath is the query-language for XML:

Read MDN: Introduction to using XPath in JavaScript with examples:

xml = "<Query><Where><And><In><FieldRef Name='Title' /><Value Type='Text'>kk</Value></In><And><In><FieldRef Name='DocIcon' /><Value Type='Computed'>bb</Value></In><And><In><FieldRef Name='Continent' /><Value Type='Choice'>Europe</Value></In><Eq><FieldRef Name='Constituency' /><Value Type='Choice'>Jordan</Value></Eq></And></And></And></Where></Query>"

const parser = new DOMParser();
let xmlDoc = parser.parseFromString(xml,"text/xml");

const xpathExpression = '//And[Eq/FieldRef]';
var xpathResult = xmlDoc.evaluate( xpathExpression, xmlDoc, null, XPathResult.ANY_TYPE, null );
console.log(xpathResult.resultType);  // 4 for UNORDERED_NODE_ITERATOR_TYPE 
console.log(xpathResult.iterateNext()); 

Try and test the XPath online: xpather.com. See also XPath Cheatsheet

hc_dev
  • 8,389
  • 1
  • 26
  • 38