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

3

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.

Basham answered 28/3, 2018 at 12:52 Comment(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?Highbrow
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.Highbrow
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/….Highbrow
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 typeHighbrow
@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!Basham
So with what kind of value do you start with on the Java side of your code? Do you have a Java String there or some kind of Java number value, which type exactly? We can only help using the right way with the Saxon API and the XSLT/XPath type system if we know which kind of value you have originally as the input in Java.Highbrow
Right now in my JUnits the value for $a is 2179.125955 which gives an error when passed as String using getStringValue() method. If I use getDoubleValue(), it gives me an error with the result of $b*$c, values of $b,$c are 507.327,2179.125955. And I have java String data type from values.Basham
L
1

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.

Longitude answered 6/4, 2018 at 15:29 Comment(3)
because of this change, my other rules are failing.Looking into it.Basham
and I used BigDecimal, what is the package info of BigDecimalValue class?Basham
it is net.sf.saxon.value, i.e., this is Saxon-specific.Longitude
E
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())
Exult answered 28/3, 2018 at 12:56 Comment(2)
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!Basham
Unfortunately, I can not change the rule. Please find my updated question as I tried few more things to solve the issue.Basham
H
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)));
Highbrow answered 6/4, 2018 at 11:20 Comment(2)
I will try this solution and will let you know if it works!!Basham
some rules, which were passing before implementing this, have started failing, need to debug more on this. Thank you for your help!Basham
L
1

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.

Longitude answered 6/4, 2018 at 15:29 Comment(3)
because of this change, my other rules are failing.Looking into it.Basham
and I used BigDecimal, what is the package info of BigDecimalValue class?Basham
it is net.sf.saxon.value, i.e., this is Saxon-specific.Longitude

© 2022 - 2024 — McMap. All rights reserved.