- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
An attractive concept of tables has been implemented as GlideRecord in ServiceNow. The object comprises of functions, elements and methods to work with all fields available. As tables contain fields, and the fields have types, we tend to assume that table [dot] field will inherit that field type (e.g. string). If you think that, you backed the wrong horse! GlideRecord fields are not string, number, or boolean fields. I can confirm a GlideRecord field is represented by a GlideElement, so each field itself is an object. When used, Java will guess and cast them to the correct type. To "cast" means to take an object from one particular type and transform it into another, like dollars ($) to pounds ( £), you can lose something in the conversion.
There is no need to cry a river about it; just be aware of this behavior. Even professional developers will fail to spot these problem. Same as when you are driving and looking out for motorbikes, when you are using a GlideRecord, make sure you look out for operations on these GlideElement objects. As it reads on the THINK! campaign, expect the unexpected.
What to look out for when using a GlideRecord
# | THINK! To look out | THINK! Advice when you are scripting |
1 | GlideRecord fields containing strings, numbers, or booleans, especially when passed to other functions as parameters | When passing parameters to functions, force the cast to the required variable input type. For example, to cast to string use .toString(). To cast to decimals use value + 0. |
2 | Undefined values | When a functions does not exist, the result is undefined |
3 | The typeof the GlideRecord fields is "object" (e.g typeof gr.incident = object) | Operations that depends on the type of the object like === could fail on GlideRecord fields |
4 | Strings operations need to be applied to strings and not the GlideElement object | Ensure you are performing a .toString() when a string operation is required (e.g. gr.short_description.toString().length) |
Casting issues when using GlideRecord
Using an example, I will try to validate these three potential casting problems:
- In a GlideElement for a string field: When trying to use 'String' function 'length' on it, it returns undefined.
- In a GlideElement for numbers: When trying to compare the value using === , it returns false.
- In a GlideElement for a boolean: When trying to compare the value using === , it returns false.
Example of GlideRecord fields returning false/undefined results
To demonstrate the cast problems, I have created an example. When scripting, you can explicitly cast your field. Here is a script include I have used to explicitly cast the fields:
Script include: ParseGlideElement
Example Script:
var ParseGlideElement = Class.create();
ParseGlideElement.prototype = {
initialize: function() {},
// Parse. Input: A glideElement object. Output: A cast of the field value into boolean, decimal, date_time or string based on field internalType
parse: function(a) {
if(a.nil())return null;
var b = a.getED().getInternalType();
return "boolean" == b ? this.parseBool(a) :
"integer" == b ? this.parseInt(a) :
"glide_date_time" == b ? new GlideDateTime(a) :
"string" == b ? a.toString() :
"decimal" == b ? this.parseFloat(a):
a
},
parseBool: function(a) {
return "boolean" == typeof a ? a : /^(true|1|yes|on)$/i.test(a)
},
parseInt: function(a) {
return a + 0
},
parseFloat: function(a) {
return a + 0
},
type: "ParseGlideElement"
};
Here is the record I will be using to test:
When executing the following background script:
var gr = new GlideRecord("u_test_record");
gr.get('6f46dfb913e576005e915f7f3244b020'); // sys_id of the test created
var vstring = gr.u_string1;
var vinteger = gr.u_integer;
var vboolean = gr.u_truefalse;
gs.print("Test without explicitly casting fields: \n " + testGlideRecord(vstring, vinteger, vboolean).join('\n'));
var gpe = new ParseGlideElement();
vstring = gpe.parse(gr.u_string1); // casting to string based on the ED internaltype Same as gr.u_string1.toString()
vinteger = gpe.parse(gr.u_integer); // casting to integer based on the ED internaltype. Same as gr.u_integer + 0
vboolean = gpe.parse(gr.u_truefalse); // casting to boolean based on the ED internaltype
gs.print("Test explicitly casting fields: \n " + testGlideRecord(vstring, vinteger, vboolean).join('\n'));
function testGlideRecord(vstring, vinteger, vboolean) {
var message = [];
message.push(
'\nGlide record u_test_record.do?sys_id=6f46dfb913e576005e915f7f3244b020'
);
// Example 1 - Expected cast to String
message.push("\n****** Example 1 - Expected cast to String ");
message.push("gr.u_string1: " + vstring + " - typeof: " + typeof vstring);
message.push("gr.u_string1.length: " + vstring.length + " - expected: 11");
// Example 2 - Expected cast to Integer
message.push("\n****** Example 2 - Expected cast to Integer ");
message.push("gr.u_integer: " + vinteger + " - typeof: " + typeof vinteger);
message.push(vinteger + " === 77777 :" + (vinteger === 77777) + '- expected: true');
// Example 3 - Expected cast to Boolean
message.push("\n****** Example 3 - Expected cast to boolean ");
message.push("gr.u_truefalse: " + vboolean + " - typeof: " + typeof vboolean);
message.push(vboolean + " === false :" + (vboolean === false) +
'- expected: true');
return message;
}
... this is the result:
Field | Value | Scripting example simplified | Result | Expected |
u_string1 | teststring2 | gr.u_string1.length | undefined | 11 |
u_string1 | teststring2 | gr.u_string1.toString().length | 11 | 11 |
u_integer | 7777 | gr.u_integer === 7777 | false | true |
u_integer | 7777 | (gr.u_integer + 0) === 7777 | true | true |
u_truefalse | false | gr.u_truefalse === false | false | true |
u_truefalse | false | parseBool(gr.u_truefalse) === false | true | true |
As you can see there are a few cases where you need to explicitly "cast" your field types and avoid mixing pears with apples.
How to use GlideRecord when the unexpected happens
In a nutshell, here are some recommendations for using GlideRecord when encountering a casting issue:
GlideElement with | Operations to look out | Areas of problem | Recommendation |
String | concatenations or as parameters to other functions | Operations like startsWith(), endsWith(), length used directly could return undefined | Use .toString() |
Integer or Float | When used on math operations against an integer or decimal | On some operations, it could be cast to string incorrectly or validated against the incorrect type | Consider using "value + 0 " or Number (value) to force the cast to decimal |
Boolean | When used on a conditions | When on complex conditions, it could be evaluated to false | Consider transforming the field to a boolean value |
I have tested using our Istanbul release and Chrome as the browser.
Want to learn more about GlideRecords? Here are some great resources to check out:
- Product documentation: GlideRecord
- Community Code Snippets: Some Observations on parseInt, and parseFloat
- GlideRecord Query Cheat Sheet
- Product documentation: Glide class overview
- Product documentation: Devc - GlideElement
Thanks to reid (Reid Murray) for the pointers
- 3,453 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.