From a1c2b5e060e4dcac5e6fdd3301686fbc259670a0 Mon Sep 17 00:00:00 2001 From: sramazzina Date: Fri, 28 Aug 2026 20:37:51 +0000 Subject: [PATCH] Document stream fields as wrapped Java objects in the JavaScript transform Stream fields are added to the Rhino scope through Context.toObject(), so they reach the script as wrapped Java objects instead of JavaScript primitives. This makes a Boolean field always truthy and makes == / != between two fields compare object identity rather than values. Add a "Stream fields are Java objects" section to the JavaScript transform documentation describing the behaviour and the .valueOf() / parseInt() workarounds, including the null-safety check. --- .../pages/pipeline/transforms/javascript.adoc | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/javascript.adoc b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/javascript.adoc index 80ad0e901ab..5d6cfdc22c5 100644 --- a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/javascript.adoc +++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/javascript.adoc @@ -248,6 +248,47 @@ Fields must be added to the rows in the same order to keep the structure of the To add a field, define it as var in the Javascript pane, and add it as a field in the Fields table. +=== [[stream-fields]]Stream fields are Java objects + +Fields coming from the input stream are not plain JavaScript primitives: the transform hands them to the Rhino engine as *wrapped Java objects* (`java.lang.Boolean`, `java.lang.Long`, `java.lang.String`, and so on). Variables you declare yourself with `var` inside the script are plain JavaScript values instead. That difference explains a number of surprising results, and `typeof` makes it visible: + +[source, javascript] +---- +var myBoolean = false; + +typeof(myBoolean); // "boolean" - declared in the script +typeof(booleanField); // "object" - coming from the stream +---- + +Two consequences are worth knowing about. + +*A Boolean field is always truthy.* Rhino treats any wrapped object as `true`, so an `if` on a Boolean field never takes the else branch, even when the field is `false`. Comparing explicitly with `true` does work, because the equality operator unwraps the object: + +[source, javascript] +---- +if (booleanField) { ... } // ALWAYS true, even when the field is false +if (booleanField == true) { ... } // correct +if (booleanField.valueOf()) { ... } // correct +---- + +*Comparing two fields with `==` or `!=` compares object identity, not values.* Two Integer fields holding the same number are still two distinct Java objects, so `!=` reports them as different. Force a conversion to a JavaScript value first: + +[source, javascript] +---- +if (intFieldA != intFieldB) { ... } // WRONG: compares references +if (intFieldA.valueOf() != intFieldB.valueOf()) { ... } // correct +if (parseInt(intFieldA) != parseInt(intFieldB)) { ... } // correct +---- + +As a rule of thumb, call `.valueOf()` (or `parseInt()`, `parseFloat()`, `String()`) on a stream field whenever you use it in a comparison or in a boolean test. + +A field that is null in the stream is put in the script scope as `null`. Testing it with `fieldName == null` is safe, but calling any method on it - including `.valueOf()` - fails, so check for null first: + +[source, javascript] +---- +var flag = (booleanField != null) && booleanField.valueOf(); +---- + === [[numeric-values]]Numeric values Most values that are assigned in JavaScript are floating point values by default, even if you think you have assigned an integer value. If you are having trouble using == or switch/case on values that you know are integers, use the following constructs: