Some additional picky details:

The unary + operator is a numeric operator so it converts its operand to a number.  If a string operand starts with a fractional number then unary + produces a fractional number (in its canonical numeric form) and it throws away any unneeded characters.  If you want your numeric result to be an integer then you need to throw away the fractional digits by doing an integer-division by 1.  Since the integer-division operator, \, is a numeric operator it always converts its operands to numbers so you no longer need the unary + to do the conversion of a string to numeric representation.  E.g.s:

USER>w "0012.543000abc"   
0012.543000abc
USER>w +"0012.543000abc"
12.543
USER>w +"0012.543000abc"\1
12
USER>w "0012.543000abc"\1 
12
 

Internally, a %DynamicArray or %DynamicObject is usually limited only by the amount of memory that the OS is willing to allocate to the job,  Because the components of such objects are not limited by their size, some very large components need to be allocated by a non-standard allocator that does not participate with the $ZSTORAGE limit.  But some components of a %DynamicObject/Array are allocated by the usual memory allocator so %ZSTORAGE might limit the size.

Since %DynamicObject/Array classes are always stored in memory (I once built one that needed 20GB of memory) you want to limit how many large objects are active at the same time.  When you encounter a large %DynamicObject/Array you want to move its data to a file or global %Stream before you start reading in another large %DynamicObject/Array,

The %FromJSON(…) method can create a %DynamicObject/Array while reading JSON from any kind of %Stream.  The %ToJSON(…) method can write JSON text to any kind of %Stream.  A %Stream is not usually limited by the max supported length of an ObjectScript string and the %FromJSON and %ToJSON methods read/write %Stream data using buffers smaller than the max supported string length.

If you have a %DynamicObject/Array with a string component larger than the ObjectScript max string length then you can use the three argument %Get(key,default,type) method where the ‘type’ argument specifies a “stream” type.  The resulting %Stream is still stored in memory but that %Stream is readonly and will often share the memory allocation with string component in the %DynamicObject/Array. That in-memory %Stream can be copied into a file or global %Stream.  The three argument %Set(key,value,type) method can create a %DynamicObject/Array component of any length when the ‘type’ argument specifies that the ‘value’ argument is a %Stream.  If the %Set ‘value’ argument is a readonly, in-memory %Stream created by %Get(key,default,”stream”) then the component created by %Set will usually share in-memory data with its  ‘value’ argument stream.

See the class reference documentation for %Get and %Set.  The ‘type’ argument options include the capabilities to encode to, or to decode from, BASE64 representation.  This can be useful when working with a BASE64 encoded .pdf file in a JSON object.

On Caché and IRiS, both %ZU(0) and $ZU(1) will signal <FUNCTION> because they need more than 1 arugment.

On IRIS, $ZU(0,directoryName,....) with enough additional arguments will create an IRIS.DAT database file. and $ZU(1,...) will modify an existing IRIS.DAT database file.  On Caché these functions create/modify a CACHE.DAT database file.  These are legacy functions.

The Config.Database class provides ways to modify the CPF configuration file to create and configure databases.

Interactively you also use the System Management Portal (SMP) by clicking SystemAdministration > Configuration > SystemConfiguration > LocalDatabase to find the SMP webpage that can create or modify local databases.

Or you can use the DO ^|"%SYS"|DATABASE command line utility as a way to create and configure databases.

InterSystems changed how <MAXNUMBER> was signaled during conversion from text representation to numeric representation when support for 64-bit IEEE binary floating point was added to Caché.  Textual numbers that overflowed the default ObjectScript decimal representation are converted to 64-bit IEEE binary floating point which supports a much larger range of magnitudes (but about 3 fewer digits of decimal precision.)  When a literal would exceed the magnitude of IEEE floating point, the choice of whether to signal <MAXNUMBER> depends on the run-time $SYSTEM.Process.IEEEError setting since IEEE floating-point overflow can either signal <MAXNUMBER> or it can return an IEEE floating-point infinity.  When the compiler sees a numeric literal that exceeds the finite IEEE number range then the decision to signal an error is delayed until run-time execution so the current $SYSTEM.Process.IEEEError setting can be obeyed.

The quoting of the slash character, "/", is optional in JSON.  When doing a $ZCVT output conversion ,"O", in JSON IRIS chooses to not quote the slash character.  It makes the output easier to read.  However, $ZCVT doing an input conversion, "I", will recognize a quoted slash character in JSON input.  E.g.:

USER>w $zcvt("abc\/def","I","JSON")
abc/def
USER>w $zcvt("abc/def","I","JSON") 
abc/def
 

Another translation is

 Read *R:20
 ;; Test error case

 If '$Test { Use Write !!!,"Expired time." Quit }
 ;; Test character "a" case

 If $c(R)="a" {
   Use Write !!!,"A letter a has been read."
   Quit
 }
 ;; I added more code here to demonstrate "fall through" in original
 ;; when neither timeout nor "a" occurs
 Use 0
 Write !,"A character other than ""a"" was read"
 Quit

 ;; Since all 3 cases execute Use 0, this statement can be placed after Read and the other 3 copies deleted
 

I am assuming your problem is that request.HttpResponse.Data.Read() is complaining because you are reading the entire pdf file into an ObjectScript variable with its maximum supported string length of 3,641,144 characters.  You will have to read it out in smaller chunks that individually fit into an ObjectScript string.  The chunksize will be important as you pass the chunked data to $system.Encryption.Base64Encode(content) and your chunks cannot end between the boundaries between two different BASE64 encoding blocks.  The results of each Base64Encode must then be sent to some form of %Stream (probably %Stream.GlobalBinary or %Stream.FileBinary) since only a %Stream can hold a block of data larger than 3,641,144 characters.  Using a small, appropriate chuncksize will limit the in-memory resources used by this conversion.

If you don't mind having the entire PDF file in memory at one time you can use %DynamicObject to hold and decode that base64 data.  The %Library.DynamicObject and %Library.DynamicArray class objects are usually used to represent data that was originally JSON encoded.  These Dynamic Objects exist only in memory but you can serialize them into JSON textual representation using the %ToJSON(output) method.  But if the JSON text representation contains more than 3,641,144 characters then you better direct 'output' into some form of %Stream.

You can convert a binary pdf file into BASE64 encoding doing something like:

SET DynObj={}  ;; Creates an empty %DynamicObject
DO Dynobj.%Set("pdffile",request.HtttpResponse.Data,"stream")
SET Base64pdf=Dynobj.%Get("pdffile",,"stream>base64")

Then Base64pdf will a readonly, in-memory %Stream.DynamicBinary object which is encoded in BASE64.  You can use Base64pdf.Read(chunksize) to read the BASE64 out of Base64pdf in ObjectScript supported chunks.  You do not have to worry about making sure the chunksize is a multiple of 3 or a multiple of 4 or a multiple of 72.  You can also copy the data in Base64pdf into a writeable %Stream.FileBinary or a %Stream.GlobalBinary using the OtherStream.CopyFrom(Base64pdf) method call.

If your HttpResponse contains a BASE64 encoded pdf file instead of a binary pdf file then you can do the reverse decoding by:

SET DynObj={}
DO Dynobj.%Set("pdffile",request.HtttpResponse.Data,"stream<base64")
SET BinaryPDF=Dynobj.%Get("pdffile",,"stream")

Then BinaryPDF is a readonly %Stream.DynamicBinary containing the decoded pdf data.  You can copy it to a %Stream.FileBinary object which can then be examined using a pdf reader application.

A canonical numeric string in ObjectScript can have a very large number of digits.  Such a string can be sorted with ObjectScript sorts-after operator, ]], and reasonably long canonical numeric strings can be used as subscripts and such numeric subscripts are arranged in numerical order before all the subscript strings that do not have the canonical numeric format.

However, when ObjectScript does other kinds arithmetic on a numeric string then that string is converted to an internal format, which has a restricted range and a restricted precision.  ObjectScript currently supports two internal formats.  The default format is a decimal floating-point representation with a precision of approximately 18.96 decimal digits and a maximum number about 9.2E145.  For customers doing scientific calculations or needing a larger range, ObjectScript also supports the IEEE double-precision binary floating-point representation with a precision around 16 decimal digits and a maximum number about 1.7E308.  You get the IEEE floating-point representation with its reduced precision but its greater range by using the $double(x) function or doing arithmetic on a string which would convert to a numeric value beyond the range of the ObjectScript decimal floating-point representation.  When doing arithmetic that combines ObjectScript decimal floating-point values with IEEE binary floating-point values then the decimal floating-point values will be converted to IEEE binary floating point before performing the arithmetic operation.

Here are more picky details.

The ObjectScript decimal floating-point representation has a 64-bit signed significand with a value between -9223372036854775808 and +9223372036854775807 combined with a decimal exponent multiplier between 10**-128 and 10**127.  I.e., a 64-bit twos-complement integer significand and a signed byte as the decimal exponent.  This decimal floating-point representation can exactly represent decimal fractions like 0.1 or 0.005.

The IEEE binary floating-point representation has a sign-bit, an 11-bit exponent exponent encoding, and a 52 bit significand encoding. The significand usually encodes a 53-bit range values between 1.0 and 2.0 - 2**-52 and the exponent usually encodes a power-of-two multiplier between 2**1023 and 2**-1022.  However, certain other encodings will handle +0, -0, +infinity, -infinity and a large number of NaNs (Not-a-Number symbols.)  There are also some encodings with less than 53 bits of precision for very small values in the underflow range of values.  IEEE 64-bit binary floating-point cannot exactly represent most decimal fractions.  The numbers $double(0.1) and $double(0.005) are approximated by values near 0.10000000000000000556 and 0.0050000000000000001041.

I have written some ObjectScript code that can do add, subtract and modulo on long canonical numeric strings for use in a banking application.  However, if you are doing some serious computations on large precision values then you should use the call-in/call-out capabilities of IRIS to access external code in a language other than ObjectScript. Python might be a good choice.  You could use canonical numeric strings as your call-in/call-out representation or you could invent a new encoding using binary string values that could be stored/fetched from an IRIS data base.

ObjectScript was designed to do efficient movements and rearrangements of data stored in data bases.  If you are doing some serious computations between your data base operations then using a programming language other than ObjectScript will probably provide better capabilities for solving your problem.

The ObjectScript $ZDATETIME function (also-know-as $ZDT) contains lots of options, some of which are close to what your want.  [[ Note $HOROLOG is also-known-as $H; $ZTIMESTAMP is aka $ZTS. ]]

$ZDT($H,3,1) gives the current local time, where character positions 1-10 contain the date you want and positions 12-19 contain the time you want.  However, character position 11 contains a blank, " ", instead of a "T".

$ZDT($ZTS,3,1) gives the current UTC time with character position 11 containing a blank.

Assigning
    SET $EXTRACT(datetime,11)="T"
to your datetime result will fix the character position 11 issue.

Instead of using time format 1, you can use time formats 5  and 7 with $H.  $ZDT($H,3,5) gives local time in the format you want except character positions 20-27 contain the local offset from UTC.  $ZDT($H,3,7) converts the local $H date-time to UTC date-time and makes character position 20 contain "Z" to indicate the "Zulu" UTC time zone.  However, if your local time-zone includes a Daylight Saving Time (DST) offset change when DST "falls back" causing a local hour to be repeated then the time format 5 UTC offset or the time format 7 conversion from local to UTC will probably be incorrect during one of those repeated hours.

Although the above description says $ORDER scans "down" a multidimensional global, other programers might say it scans "sideways".  There are many different structures for databases.  E.g., there are network databases (sometimes called CODASYL databases); there are hierarchical databases (like ObjectScript multidimensional arrays/globals); there are relational databases (often accessed by the SQL language); ...

ObjectScript is based on the ANSI M language standard.  I believe that the name of the ANSI M hierarchical function $QUERY has always been $QUERY but the original name of the ANSI M hierarchical function $ORDER was formerly $NEXT.  $NEXT is very similar to $ORDER but $NEXT had problems with its starting/ending subscript values.  IRIS ObjectScript no longer documents the obsolete $NEXT function but the ObjectScript compiler still accepts programs using $NEXT for backwards compatibility.

Consider the following ObjectScript global array:

USER>ZWRITE ^g
^g("a")="a"
^g("a",1)="a1"
^g("b",1)="b1"
^g("b",1,"c")="b1c"
^g("c")="c"
^g("c","b10")="cb10"
^g("c","b2")="cb2"
^g("d",2)="d2"
^g("d",10)="d10"

Consider the following walk sideways by $ORDER along the first subscript of ^g"

USER>set s=""
USER>for { SET s=$ORDER(^g(s))  QUIT:s=""  WRITE $NAME(^g(s)),! }    
^g("a")
^g("b")
^g("c")
^g("d")

Although these 4 globals contain values below them, the $ORDER walks did not walk down to deeper subscripts.  As it walked sideways, it returned the subscripts "b" and "d" even though ^g("b") and ^g("d") did not have values of their own but only values underneath them.

Now consider the walk down deeper by $QUERY through all the subscripts of ^g(...) at all subscript levels:

USER>set s="^g"
USER>for { WRITE s,!  SET s=$QUERY(@s)  QUIT:s="" }               
^g
^g("a")
^g("a",1)
^g("b",1)
^g("b",1,"c")
^g("c")
^g("c","b10")
^g("c","b2")
^g("d",2)
^g("d",10)

This walk by $QUERY was told to start at ^g and every call on $QUERY went through every subscripted node of ^g(...) that contained a value regardless of the number of subscripts needed.  However, elements ^g("b") and ^g("d") that did not contain values of their own were skipped by the $QUERY walk as it continued on to nodes with deeper subscripts that did contain values.

Also note that each $ORDER evaluation returned only a single subscript value as it walked sideways while each $QUERY evaluation returned a string containing the variable name along with all the subscript values of that particular multidimensional array node.

You might consider using the %Library.DynamicObject and %Library.DynamicArray classes which are built into ObjectScript.  ObjectScript supports JSON constructors for %DynamicObject, {...}, and for %DynamicArray, [...].  a %DynamicObject/Array can contain both JSON values and ObjectScript values.   There is also a %FromJSON(x) which reads JSON objects/arrays when x is a %Stream or a FileNme or an ObjectScript %String containing JSON syntax.  Here is an example from a recent IRIS release:


USER>zload foo

USER>zprint
foo()    {
            set DynObj = {
            "notanumber":"28001",
            "aboolean":true,
            "anumber":12345,
            "adecimal":1.2,
            "adate":"01/01/2023",
            "adate2":"01-01-2023",
            "anull":null,
            "anull2":null,
            "anull3":null
            }
            write DynObj.%ToJSON(),!,!

            set DynObj.Bool1 = 1    ; Set without type
            do DynObj.%Set("Bool2",1,"boolean") ; Set with type
            set DynObj.old1 = "abc"   ; Set without type
            do DynObj.%Set("new1","abc","null") ; Set with type
            set DynObj.old2 = ""   ; Set without type
            do DynObj.%Set("new2","","null") ; Set with type
            write DynObj.%ToJSON()
}        

USER>do ^foo()
{"notanumber":"28001","aboolean":true,"anumber":12345,"adecimal":1.2,"adate":"01/01/2023","adate2":"01-01-2023","anull":null,"anull2":null,"anull3":null}

{"notanumber":"28001","aboolean":true,"anumber":12345,"adecimal":1.2,"adate":"01/01/2023","adate2":"01-01-2023","anull":null,"anull2":null,"anull3":null,"Bool1":1,"Bool2":true,"old1":"abc","new1":"abc","old2":"","new2":null}

Your can use ordinary ObjectScript assignment to assign ObjectScript values to entries in a %DynamicObject/Array.  You can use the %Set(key,value,type) method to assign ObjectScript values into an object with an optional type parameter controlling which JSON value to which to convert the ObjectScript value.  I.e., type "boolean" converts ObjectScript numeric expressions into JSON false/true values and type "null" converts an ObjectScript %String into a JSON string *except* that the empty string is converted to the JSON null value.  (Note:  some older implementations of %Set with type "null" only accepted "" as the value while new implementations accept any string expression as the value.)

The JSON constructor operators in ObjectScript, besides accepting JSON literals as values, can also accept an ObjectScript expression enclosed in round parentheses. 

E.g.,  SET DynObj2 = { "Pi":3.14159, "2Pi":(2*$ZPI) }

When asking about a 'ROUTINE' you may be asking about the difference between a 'Routine', 'Procedure', 'Subroutine', 'Function', 'Label', 'Class' and 'Method'

A 'Routine' is a source code file with name like 'myroutine.mac'.  Source code can also be a 'Method' which is found in a 'Class' file with a name like 'myclass.cls"

A Routine file can contain Procedures, Subroutines, Functions and Labels.  See https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS_usercode#GCOS_usercode_overview for some InterSystems documentation.

You can call a subroutine with the statement 'DO MySubroutine^myroutine(arg)'; You can call a function with the expression '$$MyFunction^myroutine(arg)';  You can call the procedure 'MyProcedure^myroutine(arg)' using either the syntax for a subroutine call or function call depending on whether you need the value returned by the procedure; and, You can Goto the source code following a label with the statement 'GO MyLabel^myroutine'.  If you reference a subroutine/function/procedure/label inside a Routine file (e.g. myroutine.mac) and that subroutine/function/procedure/label access (e.g.$$MyFunction^myroutine(arg)) is referencing a name defined in the same Routine file then you do not have to specify the ^Routine name in the call syntax (e.g. $$MyFunction(arg)).

The local variables used inside a subroutine or function are 'public' variables and those named variables are shared with all other subroutines and functions.  You can use the NEW command inside a subroutine or function to temporarily create new public variables without modifying previous instances of public variables with the same names.

The local variables used inside a procedure are private variables.  The private variables are only available inside the procedure.  Those names do not conflict with variables used by any caller of the procedure.  Those names are not available in any code called by the procedure although it is possible to pass either the private variable or the value of the private variable as an argument when calling out of a procedure.  The declaration of a procedure can optionally include a list of public variables.  Names in public list reference the public variable when they are accessed by code in the procedure.  You can use the NEW command with an argument that is a public variable within a procedure.  A label defined inside a procedure is also private and it cannot be referenced by any GO command outside that procedure.

Methods defined in a class file default to being a procedure with private variables and private labels.  However, it is possible to specify that a method is a subroutine or function.   Also, a method procedure declaration can optionally include a list of global variables.

As mentioned in another Developer Community Question, a recent version of IRIS would allow you to  evaluate object.%Get("pdfKeyName",,"stream") which would return to you an %Stream object containing the JSON string in question as raw characters.  Also, %Get in IRIS can support object.%Get("pdfKeyName",,"stream<base64") which would do Base64 decoding as it creates the %Stream. However, you said you need to stick with an older version of Caché/Ensemble which predates these %Get features.

It is possible to convert the long JSON string component to a %Stream but it will take some parsing passes.

(1) First use SET json1Obj=[ ].%FromJSON(FileOrStream) to create json1OBJ containing all the elements of the original JSON coming from a File or Stream.

(2) If your pdf JSON string is nested in json1OBJ then select down to the closest containing %DynamicObject containing your pdf JSON string,  I.e. Set json2Obj=json1Obj.level1.level2 if your original JSON looks like {"level1":{"level2":{"pdfKeyName":"Very long JSON string containing pdf", ...}, ...}, ...}

(3) Create a new %Stream containing the JSON representation of that closest containing %DynamicObject.  I.e.,

   SET TempStream=##class(%Stream.TmpBinary).%New()
   DO json2Obj.%ToJSON(TempStream)

(4) Read out buffers from TempStream looking for "pdfKeyName":" .  Note that this 14-character string could span a buffer boundary.

(5) Continue reading additional buffers until you find a "-characer not preceded by a \-character; Or until you find a "-character preceded by an even number of \-characters.

(6) Take characters read in step (5) and pass them through $ZCVT(chars,"I","JSON",handle) to convert JSON string escape characters to binary characters and then write them to a %Stream.FileBinary (or some other Binary %Stream of your choosing.)

(7) If your JSON "pdfKeyName" element contains Base64 encoding  then you will also need to use $SYSTEM.Encryption.Base64Decode(string). Unfortunately $SYSTEM.Encryption.Base64Decode(string), unlike $ZCVT(chars,"I",trantable,handle), does not include a 'handle' argument to support the buffer boundary cases in those situations where you have broken a very large string into smaller buffers.  Therefore you should remove the whitespace characters from 'string' before calling $SYSTEM.Encryption.Base64Decode(string) and you must make sure the argument 'string' has a length which is a multiple of 4 so that a group of 4 data characters cannot cross a buffer boundary.

Again the more complete support in IRIS can do steps (2) through (7) without worrying about buffer boundaries by executing

   Set BinaryStreamTmp = json1Obj.level1.level2.%Get("pdfKeyName",,"stream<base64")

and you can then copy the BinaryStreamTmp contents to wherever you want to resulting .pdf file.

The latest version of IRIS will have improved %DynamicObject (and the %DynamicArray) class objects.  They will support a method call like obj.%Get(key,,”stream”) which will return a %Stream.DynamicCharacter oref and this %Stream can contain a very large number of characters.  This can be copied into a %Stream.GlobalCharacter or a %Stream.FileCharacter if you want to save those characters in a persistent object.

This new form of %Get will also be able to include encoding/decoding using Base64 representation.  Similar extensions have been added to the %Set method.

The JSON classes in Ens.Util.JSON,  %ZEN.Auxiliary.json* and %JSON.* all contain methods that convert JSON representation to/from ObjectScript classes.  Once you have an ordinary ObjectScript Class then you are using ObjectScript data types for Property values.  The JSON null is usually converted to "" (null string).  Also, ordinary Property variables of an ObjectScript Class are never undefined but are automatically initialized to "" (the null string).  [[ An exception is [MULTIDIMENSIONAL] Properties which can be undefined but by default such Property variables do not participate in %Save() and JSON/XML export/import operations. ]]  SQL operations involving Class properties treat "" (the null string) as the SQL NULL value and SQL assumes a Class Property containing the ObjectScript string $C(0) is the empty string.

[[ Although the original question involved Caché and not IRIS, IRIS has signifcantly more complete support for the %DynamicAbstractObject classes so my examples will use IRIS.  If possible, I recommend upgrading to IRIS. ]]

There is the class %Library.DynamicAbstractObject and its subclasses %DynamicArray and %DynamicObject that can contain elements which can either be JSON values or ObjectScript values.  The ObjectScript statement:

USER>SET x={"a":null,"b":"","e":1.0,"f":"1.0","g":(00.1),"h":($c(0))}

makes x be a %DynamicObject oref where element a is a JSON null and where element "g" is the ObjectScript number .1 and element "h" is the ObjectScript string $c(0).  Note that if an ObjectScript expression is a %DynamicObject constructor then ObjectScript parses the elements of that constructor using JSON syntax except for the extension to constructor syntax where an element inside round parentheses is parsed as an ObjectScript run-time expression generating an ObjectScript value.

You can convert a %DynamicObject to JSON string representation and any ObjectScript valued element will be converted to JSON representation.

[[ Note that JSON does not support certain ObjectScript values: $double("NAN"), $double("INFINITY") and orefs that are not a subclass of %DynamicAbstractObject.  A %DynamicAbstractObject containing such an ObjectScript element cannot be converted to JSON. ]]

USER>WRITE x.%ToJSON()
{"a":null,"b":"","e":1.0,"f":"1.0","g":0.1,"h":"\u0000"}

Evaluating a %DynamicObject element in an ObjectScript expression converts that element to an ObjectScript value.

USER>ZWRITE x.a, x.b, x.c, x.e, x.f, x.g, x.h
""
""
""
1
"1.0"
.1
$c(0)

Notice that the undefined element x.c is converted to the ObjectScript null string.  You never get an error evaluating x.%Get(key) for any value of the expression key as every undefined element in a %DynamicObject has the value of the null string.  Also, x.a, which contains a JSON null, is converted to the ObjectScript null string.  The JSON treatment of undefined elements and the ObjectScript treatment of undefined Properties means that when we convert an ordinary ObjectScript class to either XML or JSON then we can skip converting a Property with the null string value as converting JSON or XML back to an ordinary class object will result in all unrepresented properties getting the value of the null string.

If you need to know if a %DynamicObject element is JSON null, null string or undefined then evaluating the %GetTypeOf(key) will tell you that.

USER>ZWRITE x.%GetTypeOf("a"),x.%GetTypeOf("b"),x.%GetTypeOf("c"),x.%GetTypeOf("e")
"null"
"string"
"unassigned"
"number"

The %FromJSON(stream)/%ToJSON(stream) methods will let you read/write JSON representation from/to a %Stream.

[[ Things that only work in IRIS follows. ]]

The size of the %DynamicArray/%DynamicObject class objects is limited only by the amount of memory available to your process.  A string valued %DynamicObject element can be significantly longer than the maximum length supported by ObjectScript string values.  If you have such a long string element then you will have convert that element to an ObjectScript %Stream in order to manipulate it in ObjectScript.

USER>SET stream=x.%Get(key,,"stream")  ;; Note 3 arguments with 2nd argument missing

will generate an in-memory, readonly %Stream that can be copied to a Global or File %Stream or can be examined by reading that string in small pieces.

In recent IRIS releases you can do

USER>SET binarystream=x.%Get(key,,"stream<base64")

which will convert a base64 encoded element into a readonly binary %Stream.  You can do the reverse conversion by evaluating x.%Set(key,binarystream,"stream>base64").  See the the Class Reference documentation pages for the %Library.%DyanmicAbstractObject class and its %DynamicArray and %DynamicObject subclasses for more details.

By the looks of it, the original question applied $DATA to an element of the %DynamicAbstractObject classes.  The $DATA and $GET functions can only be applied to Multidimensional variables.  All ObjectScript local and global variables are Multidimensional.  By default a property variable of a class object is not multidimensional unless the property default is over ridden with the [ Multidimensional ] attribute.  The %DynamicAbstractObject classes provide no way of supporting elements which are Multidimensional.  If dynobj is an object reference to a %DynamicObject then dynobj.%Get(key) is defined for all possible string values of 'key'.  If dynobj.%GetTypeOf(key) returns "unassigned", or "null", then dynobj.%Get(key) will return the empty string value.

Currently, $DATA(dynobj.keyname) signals <PROPERTY DOES NOT EXIST> which is what is signaled for by all classes if the Property 'keyname' does not exist.  In a future IRIS release it will report

<INVALID CLASS> *Class '%Library.DynamicObject' does not support MultiDimensional operations

The correct way to see if keyname "test" exists in a %DynamicObject is to evaluate (dynobj.%GetTypeOf("test") '= "undefined") .

You are using a 5 year old version of Ensemble that is using an experimental release of JSON support with classes named %Library.AbstractObject, %Library.Object and %Library.Array.  When you upgrade to any version after 2016.1 you will find the JSON support classes are now named %Library.DynamicAbstractObject, %Library.DynamicObject and %Library.DynamicArray.  All of the method names will change because support for system methods with names that start with $ has been removed.  Method names like  $getIterator, $fromJSON and $getNext will become %GetIterator, %FromJSON and %GetNext.  Other than the extensive name changes, most of features of the experimental JSON classes has remained unchanged.

There are additional new features added during future releases. For example, the ObjectScript language will accept JSON object and array constructor syntax as part of the ObjectScript expression syntax.  If an element which is part of an object/array constructor inside an ObectScript program is enclosed in round parentheses then that element is evaluated using ObjectScript expression syntax instead using JSON literal syntax.  There will also be some additional ways to control the conversions between JSON types and ObjectScript types.

Standard class object properties have a limited string length but a property can contain a %Stream.GlobalCharacter oref which %JSON.Adaptor can export as a JSON string.

Another option is to create a %DynamicObject (%Library.DynamicObject) class object or a %DynamicArray (%Library.DynamicArray) class object instead of using a subclass of %JSON.Adaptor.  You can create a %DynamicObject/%DynamicArray is ObjectScript by using a JSON object/array literal as an ObjectScript literal.  The %FromJSON class method will create a %DynamicObject/%DynamicArray by importing JSON from a %Stream, file or device and the %ToJSON class method will export a %DynamicObject/%DynamicArray to a %Stream, file or device.

On IRIS, the %Set and %Get methods in %DynamicObject-s/%DynamicArray-s have been extended to take type keywords of the form "stream", "stream>base64", "stream<base64" which can transfer %DynamicObject string elements between unlimited length %Stream-s and include the ability to encode or decode Base64 during the transfer.  There are also type key words "string", "string>base64", "string<base64" which can set/get ObjectScript string values into/from %DynamicObject elements but ObjectScript strings are currently limited to a length of 3,641,144 characters.

So on IRIS you can do something like:
   Set DynObj = {"ID":23, "Name":"John Doe", "BirthDay":"1974-12-15"}
   Do DynObj.%Set("DataFile",DataOref,"stream>base64")
   Do DynObj.%ToJSON(OutputOref)
where DataOref is a class reference to a %Stream.FileBinary referencing the binary file containing data related to John Doe and where OutputOref is a %Stream.FileCharacter referencing the file that will contain JSON text.

There are actually two (maybe more) levels of ObjectScript.  There is basic ObjectScript, which is an very extended version of the ANSI M language.  [ footnote [ANSI M is the successor of the ANSI MUMPS language and that language, without the large number of extensions supported by basic ObjectScript, could be considered to be a third language level although no modern programmer would restrict their code to this much older language definition.] ]  And there is the Class Language ObjectScript, which includes things like type-name classes:  %Library.String (can be abbreviated %String), %Library.Integer (can be abbreviated %Integer), etc.  It also includes Class Methods (with syntax like ##class(Class.Name).ClassMethodName(arg1,arg2)) and there are Object Methods (which look like oref.ObjectMethodName(arg1,arg2), where oref must contains an extended ObjectScript object reference) and there are Object Properties (which look like oref.PropName, where oref must contain an extended ObjectScript object reference.)

An example of a basic ObjectScript statement is SET var1=42,var2="42"  Almost every operation in basic ObjectScript thinks var1 and var2 contain identical values.  So the string equality operation var1=var2 returns 1 because var1 is converted to a string and "42" equals "42".  The numeric comparison operations var1<var2 and var1'<var2 return 0 and 1 because var2 is converted to a numeric and 42<42 is false while 42'<42 is true.  We could also evaluate var1+var2 and the result will be 84 (or is that result "84"--who can tell?).

In Class Language ObjectScript you can declare
          property LimitedInt : %Integer(MAXVAL=10);
and if you (directly or indirectly) call the %ValidateObject() method on a class object containing the property LimitedInt then the contents of LimitedInt may be checked to make sure they look like an integer with a value not greater than 10.  However, only Class Language methods like %SerializeObject, %ValidatObject, %Save, etc. make these checks on the value of LimitedInt.  If someone executes
          SET oref.LimitedInt=20.95
in basic ObjectScript, the basic ObjectScript execution will not signal an error despite the fact that 20.95 is larger than the MAXVAL and despite the fact that 20.95 is NOT a %Integer.  Only executing an appropriate Class Language method will detect that oref.LimitedInt does not contain a valid value.  The purpose of %Library.DataType subclasses is to make it possible that a %Save() method does not save invalid property values into a data base.

Certain ObjectScript conversions may change an ObjectScript value.  This can happen when changing a well-formed numeric string to be a decimal number because the decimal arithmetic implemented in ObjectScript mathematics supports no more than 19 decimal digits of precision.  Consider,

USER>set a="12345678901234567890123",b="12345678901234567890124",c="123456789012345678901230"
USER>write b>a," ",a>b," ",+a," ",+b
0 0 12345678901234567890000 12345678901234567890000

Because the > and < operators are arithmetic-comparison operators the string operands are converted to a numeric value with only 19 significant digits of precision and the resulting numeric values for a and b end up being equal so neither b>a nor a>b are true.  However, the sorts-after operator, ]], orders the canonical numeric strings before any non-empty string that does not have canonical numeric syntax.  The canonical numeric strings are sorted in numeric order while strings that do not have canonical numeric syntax are sorted in textual string order.  This is also the default rule for ordering subscript values in ObjectScript.  Operands of ]] are converted to strings before doing  this subscript ordering.  The values a, b and c are all canonical numeric strings and ObjectScript is perfectly capable of doing the sorts-after string comparisons on very long canonical numeric strings.  Consider,

USER>write a]]b," ",b]]a," ",b]]c," ",c]]b
0 1 0 1

Now a and b are both canonical numeric strings with 23 digits and their first 22 digits are equal but the b has a 23rd digit larger than the 23rd digit of a, so b sorts-after a.  But b does not sorts-after c because c has more significant digits so the canonical numeric value of c is greater than the canonical numeric value of b.

The basic ObjectScript language has a very small class of built-in types.

(1) There are the ObjectScript string values, which include the subclass of canonical numeric strings.

(2) There are the default decimal floating-point values which do not have more 19 digits of decimal precision (and may sometimes have less than 19 digits of precision because the implemented accuracy of ObjectScript decimal arithmetic operations is approximately 18.96 decimal digits.)  Every decimal floating-point value can be converted exactly to a canonical numeric string (but canonical numeric string values with more than 18 digit characters cannot always be converted exactly to a decimal floating-point numeric result but must instead be a decimal numeric value that is an approximation.

(3) Basic ObjectScript supports a third set of "$DOUBLE values" which contains the *binary* floating-point values specified by the IEEE 64-bit binary floating-type type.  The 64-bit binary floating-point arithmetic specified by the IEEE standard has approximately 15.95 decimal digits of precision but since the representation is binary, and not decimal, an exact conversion of a 64-bit IEEE binary floating-point value to a decimal string can have over 1000 digits.  Now every $DOUBLE binary floating-point value could be exactly converted to a canonical numeric string but it is not reasonable to have such a conversion produce such long strings.  The default conversion of a $DOUBLE value to a canonical numeric string will have no more than 20 significant digits, and the approximated 20th significant digit will never be a 5 or 0 (unless that 20th digit results in an exact conversion.)  Using a default canonical numeric string with 20 significant digits for $DOUBLE conversions means the ]], sorts-after, operator will correctly order a $DOUBLE binary floating-point value in-between the adjacent 19-digit decimal floating-point values of the ObjetScript default decimal numeric type.

(4) There are also the oref values which are the basic ObjectScript values created by Class Language ObjectScript.  Basic ObjectScript can do property evaluation, property assignment and method calls using the basic ObjectScript oref type.  Basic ObjectScript can convert oref values to the ObjectScript string type and the ObjectScript decimal arithmetic type but neither of these conversions is particularly useful unless you are debugging.

The ClassMethod IsNumeric(...) seems to contain three SET statements that do nothing more than copy data around.  The following is equivalent (without data copies)

ClassMethod IsNumeric(value As %StringAs %Boolean
 { QUIT $ISVALIDNUM(value)  }

And this implementation of IsNumeric is equivalent to the first answer, ClassMethod  IsValidNumber(...) by  Eduard Lebedyuk except the IsNumeric ClassMethod does not have the [ FINAL ] attribute.