xpath icon indicating copy to clipboard operation
xpath copied to clipboard

Implement if-then-else Xpath expression

Open davidzr opened this issue 3 years ago • 7 comments

support if expressions

davidzr avatar Jan 06 '21 19:01 davidzr

Coverage Status

Coverage decreased (-0.4%) to 76.3% when pulling 69c2a883fc71bcad06c28040db25dc36c3a65d51 on davidzr:master into cd2afb821d43992abcefe1c70b1b41af5b640179 on antchfx:master.

coveralls avatar Jan 06 '21 19:01 coveralls

Hello, thanks for your contribution first. I am not familiar with if-else-then and do not fully understand where it can apply. So can your give some example.

I am writing some test code to test if-then-else to test, Please forgive me if I'm spoke wrong.

example 1:

	s := `<aa/><a id="1"><aa>1</aa></a><a id="2"><bb/></a>`
	doc, err := xmlquery.Parse(strings.NewReader(s))
	exp, err := xpath.Compile("if (//a[@id='2']) then 1 else 2")
	if err != nil {
		panic(err)
	}
	fmt.Println(exp.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64))

output: 1

The above code expected value is "2" but got "1".

example 2:

s := `<aa/><a id="1"><aa>1</aa></a><a id="2"><bb/></a>`
	doc, err := xmlquery.Parse(strings.NewReader(s))
	exp, err := xpath.Compile("if (//a[@id='1']) then //aa else //bb")
	if err != nil {
		panic(err)
	}
	//fmt.Println(exp.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64))

	iter := exp.Select(xmlquery.CreateXPathNavigator(doc))
	for iter.MoveNext() {
		n := iter.Current().(*xmlquery.NodeNavigator)
		fmt.Printf(n.Current().OutputXML(true))
	}

output

<aa></aa><aa>1</aa>

In my mind, the above code should output one "aa" but got two "aa".

zhengchun avatar Jan 07 '21 10:01 zhengchun

Hi, thanks!

I'm implementing it because xml e-invoices requested by governments do a lot of crazy validations like in [1]

I implemented it as follows:

if (test) then pass else nopass if the test returns true, then do wathever is "then" expression, otherwise do whatever if in "else" expression. In your case if (//a[@id='2']) then 1 else 2 is the same as: if (exists(//a[@id='2'])) then 1 else 2 therefore returns "1". So in if (//a[@id='1']) then //aa else //bb would return all //aa nodes.

But I think you are right, I should add context to the return expression, so in case of:

if (//a[@id='1']) then . else false() should return the <a id="1"><aa>1</aa></a> node.

Let me work on this and I will update you once I get it done.

[1] Algorithm to verify citizen id: "if (number(string-length($nitwithout)) = 5) then (number($a) * 19) + (number($b) * 17) + (number($c) * 13) + (number($d) * 7) + (number($e) * 3) else if (number(string-length($nitwithout)) = 6) then (number($a) * 23) + (number($b) * 19) + (number($c) * 17) + (number($d) * 13) + (number($e) * 7) + (number($f) * 3) else if (number(string-length($nitwithout)) = 7) then (number($a) * 29) + (number($b) * 23) + (number($c) * 19) + (number($d) * 17) + (number($e) * 13) + (number($f) * 7) + (number($g) * 3) else if (number(string-length($nitwithout)) = 8) then (number($a) * 37) + (number($b) * 29) + (number($c) * 23) + (number($d) * 19) + (number($e) * 17) + (number($f) * 13) + (number($g) * 7) + (number($h) * 3) else if (number(string-length($nitwithout)) = 9) then ((number($a) * 41) + (number($b) * 37) + (number($c) * 29) + (number($d) * 23) + (number($e) * 19) + (number($f) * 17) + (number($g) * 13) + (number($h) * 7) + (number($i) * 3)) else if (number(string-length($nitwithout)) = 10) then ((number($a) * 43) + (number($b) * 41) + (number($c) * 37) + (number($d) * 29) + (number($e) * 23) + (number($f) * 19) + (number($g) * 17) + (number($h) * 13) + (number($i) * 7) + (number($j) * 3)) else if (number(string-length($nitwithout)) = 11) then ((number($a) * 47) + (number($b) * 43) + (number($c) * 41) + (number($d) * 37) + (number($e) * 29) + (number($f) * 23) + (number($g) * 19) + (number($h) * 17) + (number($i) * 13) + (number($j) * 7) + (number($k) * 3)) else if (number(string-length($nitwithout)) = 12) then ((number($a) * 53) + (number($b) * 47) + (number($c) * 43) + (number($d) * 41) + (number($e) * 37) + (number($f) * 29) + (number($g) * 23) + (number($h) * 19) + (number($i) * 17) + (number($j) * 13) + (number($k) * 7) + (number($l) * 3)) else if (number(string-length($nitwithout)) = 13) then ((number($a) * 59) + (number($b) * 53) + (number($c) * 47) + (number($d) * 43) + (number($e) * 41) + (number($f) * 37) + (number($g) * 29) + (number($h) * 23) + (number($i) * 19) + (number($j) * 17) + (number($k) * 13) + (number($l) * 7) + (number($m) * 3)) else if (number(string-length($nitwithout)) = 14) then ((number($a) * 67) + (number($b) * 59) + (number($c * 53) + (number($d) * 47) + (number($e) * 43) + (number($f) * 41) + (number($g) * 37) + (number($h) * 29) + (number($i) * 23) + (number($j) * 19) + (number($k) * 17) + (number($l) * 13) + (number($m) * 7) + (number($n) * 3))) else if (number(string-length($nitwithout)) = 15) then ((number($a) * 71) + (number($b) * 67) + (number($c) * 59) + (number($d * 53) + (number($e) * 47) + (number($f) * 43) + (number($g) * 41) + (number($h) * 37) + (number($i) * 29) + (number($j) * 23) + (number($k) * 19) + (number($l) * 17) + (number($m) * 13) + (number($n) * 7) + (number($o) * 3))) else ''"

davidzr avatar Jan 07 '21 16:01 davidzr

I agree your point, if (//a[@id='2']) then 1 else 2 is the same as: if (exists(//a[@id='2'])) then 1 else 2. it is my mistake, if (//a[@id='2']) then 1 else 2 should return 1 not 2.

another expression on example 2: if (//a[@id='1']) then //aa else //bb, there are could be have two expression, one is query on current context(like ./), another might be query all(like //), is there any example on this case?

zhengchun avatar Jan 08 '21 11:01 zhengchun

another expression on example 2: if (//a[@id='1']) then //aa else //bb, there are could be have two expression, one is query on current context(like ./), another might be query all(like //), is there any example on this case?

Yes, one common example is to concat the returned node value:

if (//a[@id='1']) then concat(./,'-','another expression') else ''

davidzr avatar Jan 11 '21 17:01 davidzr

Are there any plans to revive this PR? If @davidzr lost interest in it, perhaps I take take a look at taking it over eventually (when I approach the need in my work)

yrashk avatar Apr 20 '21 20:04 yrashk

Hello @yrashk, I would be grateful if you can take a look at it, I don't fully understand how context works in the if-else statement.

davidzr avatar Apr 22 '21 15:04 davidzr