6

The toComplie string contains all the definitions of the functions like sum, multiply, etc. appended by if ($a > 0) then (iaf:numeric-equal(iaf:numeric-multiply($b, $c), $d)) else (true())

The snippet executing this is :

XQueryExecutable queryExecutable = xqueryCompiler.compile(toCompile.toString()); XQueryEvaluator xqueryEvaluator = queryExecutable.load(); //setExternalVariables(): function used to set the variables for the test contains below line xqueryEvaluator.setExternalVariable(new QName(memberName), value); setExternalVariables(xqueryEvaluator,assertionExpression); xqueryResult = xqueryEvaluator.evaluate(); 

Which throws an exception as below:

XPTY0004: Required item type of the first operand of '>' is numeric; supplied value has item type xs:string


Please let me know if any more information is needed to understand the question. Is this because of the else part, or something else?

EDIT: In setExternalVariables(), I'm adding the variables using below line, using for-each loop. value variable is of type net.sf.saxon.s9api.XdmValue

xqueryEvaluator.setExternalVariable(new QName(memberName), value); 

In setExternalVariables() method,

// FACT_VALUE_FORMAT:%s;%s -- where first string is value and second gives information about precision. //current option XdmAtomicValue atomicValue = new XdmAtomicValue(String.format(FACT_VALUE_FORMAT, fact.getValue(),getPrecision(fact.getDecimals()))); // alternative 1 atomicValue = new XdmAtomicValue(getDoubleValue(fact)); //alternative 2 atomicValue = new XdmAtomicValue(getStringValue(fact)); 

In getDoubleValue(),

 String precision = fact.getDecimals(); BigDecimal value = new BigDecimal(fact.getValue()); if((precision != null ) && (precision.equals(INF_STRING) == false )){ if(Integer.parseInt(precision)>0){ NumberFormat nf = NumberFormat.getNumberInstance(Locale.US); DecimalFormat df = (DecimalFormat)nf; // If the decimal value is greater than 0, then we need the decimal precision correct to n places of decimal df.setMaximumFractionDigits(Integer.parseInt(precision) + 1); double doublePrecision = Math.pow(10,-Integer.parseInt(precision))/2; df.setMaximumFractionDigits(Integer.parseInt(precision) + 1); precision = df.format(doublePrecision); System.out.println("doublePrecision\t:\t"+doublePrecision); return (double) Math.round(value.doubleValue() * doublePrecision) / doublePrecision; }else{ int scale = (int) Math.pow(10, -Integer.parseInt(precision)); System.out.println("scale\t:\t"+scale); return (double) Math.round(value.doubleValue() * scale) / scale; } } return value.doubleValue(); 

In getStringValue(),

 String value = fact.getValue(); String decimal = fact.getDecimals(); String DOT = "\\."; if(value.contains(".")){ final int parseInt = Integer.parseInt(decimal); if(parseInt>0){ String[]split = value.split(DOT); value = split[0]; if(parseInt>=value.length()){ return "0"; } for (int i = 0; i < parseInt; i++) { char[] array =value.toCharArray(); array[value.length()-i-1]="0".charAt(0); value = new String(array); } }else{ final int parseNegativeInt = -Integer.parseInt(decimal); String[]split = value.split(DOT); String tempValue = split[1]; if(tempValue.length()>parseNegativeInt){ tempValue = tempValue.substring(0, parseNegativeInt); } value = split[0]+"."+tempValue; } } return value; 

Current implementation and alternative(2) does not work for the rule mentioned above, and when I'm returning double, it transforms big numbers into expression containing char E, for e.g. 5.12344E12, which fails in other rules.

Error on line 199 of module with no systemId: FORG0001: Cannot convert string "1.089563E9" to xs:decimal: invalid character 'E' at iaf:splitValueThreshold() (module with no systemId#20) at iaf:numeric-equal() (module with no systemId#343)

Please suggest any other option.

7
  • Well, where do you set, construct or create the variable value, do you want to pass in a string or a certain number type there? Commented Mar 28, 2018 at 14:57
  • You can construct an xs:decimal in your Java code using saxonica.com/html/documentation/dotnetdoc/Saxon/Api/… and pass that in to setExternalVariable. Or you can do it on the XPath side using if (xs:decimal($a) > 0) ... or as the answer suggests using if (number($a) > 0)... although that creates an xs:double. Commented Mar 29, 2018 at 7:12
  • 1
    Sorry, I think I posted a link to the .NET API, the Java API is at saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/…. Commented Mar 29, 2018 at 7:18
  • If you don't have a number type in your Java code but a String instead then you can use saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/… to construct a certain number type Commented Mar 29, 2018 at 7:20
  • @MartinHonnen, I can not change the if condition as a business rule, so your second comment won't work I guess. Will check if the links you have shared are of any help! Commented Mar 29, 2018 at 7:30

3 Answers 3

4

Typically XPath > implicitly converts a string operand to a number, but you can force the conversion using the number() XPath function.

if (number($a) > 0) then (iaf:numeric-equal(iaf:numeric-multiply($b, $c), $d)) else (true()) 
Sign up to request clarification or add additional context in comments.

2 Comments

I can not change the rule to add number function. I tried passing the value as double in there, but if the value is too big for decimal to handle it includes "E" in the number. And this logic fails in other rules where "E" is not expected in the number. Please suggest!
Unfortunately, I can not change the rule. Please find my updated question as I tried few more things to solve the issue.
2

Assuming you have your numeric values on the Java side as Strings in e.g. String value = "2179.125955"; then I would suggest to pass them as xs:decimals to XQuery/XPath by using new XdmAtomicValue(new BigDecimal(value)) as the value you pass to setExternalVariable e.g.

xqueryEvaluator.setExternalVariable(new QName(memberName), new XdmAtomicValue(new BigDecimal(value))); 

2 Comments

I will try this solution and will let you know if it works!!
some rules, which were passing before implementing this, have started failing, need to debug more on this. Thank you for your help!
1
+50

I second Martin's suggestion (I had first overlooked BigDecimal vs. BigDecimalValue).

Some alternatives also come to my mind based on Saxon's documentation:

atomicValue = new XdmAtomicValue( new BigDecimalValue(getDoubleValue(fact)) ); 

or to avoid going through doubles or BigDecimal, something like:

atomicValue = new XdmAtomicValue( (BigDecimalValue)BigDecimalValue.makeDecimalValue(getStringValue(fact), true) ); // potentially adding an "instance of BigDecimalValue" check in the middle // or bypassing all checks with 'false' instead of 'true' 

Note: I am not sure I fully understand the issue that arises in other rules if an xs:double is passed instead of an xs:decimal. I suspect that there may be some casting back to xs:string involved in these other rules, as this is where an E could be introduced. XPath comparisons between numeric values of any types should be seamless.

In general (but I am only guessing here as I do not know the details involved, apart from seeing usage of EBA's interval arithmetic functions in the rule), I think it is probably a nice idea to align the types used in or passed to XBRL Formula rules with the original XBRL concept types (use xs:decimal for xbrli:decimalItemType (r) or xbrli:monetaryItemType (m), but xs:double for xbrli:doubleItemType, etc), as the lexical spaces will then match those used in the XBRL instances.

3 Comments

because of this change, my other rules are failing.Looking into it.
and I used BigDecimal, what is the package info of BigDecimalValue class?
it is net.sf.saxon.value, i.e., this is Saxon-specific.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.