OK, I start with 47 chars... unfortunately, I have to add 20 chars more for that (stupid) extra requirement of ignoring characters like {, [, <, etc. therefore end up with 67 chars

ClassMethod IsHalfValid(x)
{
1	s z=x,x=$replace(x,"()","") g 1:x'=z q x=""
}

ClassMethod IsFullValid(x)
{
1	s z=x,x=$replace($zstrip(x,"*e",,"()"),"()","") g 1:x'=z q x=""
}

Speed was'n asked...

In the mean time the answer is already given by Mr Maslennikov and others, but to shorten your runtime,  you could also try a "variablenless" loop, maybe this gives you some seconds more... The emphasis is on "maybe".

// if you have null-subscripts enabled
if $d(^YYY(""))!1 for i=0:1 {quit:$order(@$zr)=""} write i

// if null-subscripts are not enabled
if $d(^YYY(-9E18))!1 for i=0:1 {quit:$order(@$zr)=""} write i

// of course, you can do that on any arbitrary level too
if $d(^YYY(1,2,3,""))!1 for i=0:1 {...} write i

// the value of -9E19 ist just for explanation, use a value suitable for your needs

May I ask for the background, why do you need that node count? 

As others already wrote,

write $order(^$GLOBAL(""))

gives you the first global name accessible from a given namespace. Usually, this will be a percent-global (^%...) but if you want to get the first (or all) global, which resides in a given database, the do the same as above but with extended global access

set dir = "^^c:\databases\mydb\"  // "^^" and "the path to your CACHE/IRIS.dat file"
write $order(^$|dir|GLOBAL(""))

There are several possibilities, using WinSCP  from Windows, Linux and Samba, etc. But if "using Cache codes" (Objectscript) is a  requirement, then the simplest way is: open a TCP-(server)port on the one end and a TCP-(client)port on the other end, write and read the data, close the connections. Voila. The job is done. I do not see any problem there. OK, maybe you need to open those ports. And do not forget, USB-Sticks exists too ;-))

Yes, accepts a file too, but as the following test shows, it does not make any difference... a parsing error remains parsing error

USER>set fn="/tmp/mytest.json"

USER>open fn:"nw":1 if $test { use fn write "[0.1, .2]",! close fn write "OK" } else { write "Houston, we have..." }
OK
USER>write {}.%FromJSON(fn)

<THROW>%FromJSON+38^%Library.DynamicAbstractObject.1 *%Exception.General Parsing error 3 Line 1 Offset 7
USER 2e1>

So where is that forgiving Cache/IRIS version?

Do you have a code snippet for us? Something like

write mylist.%IsA("%ListOfDataTypes") --> 1
set item = mylist.%GetAt(2)
//
// Now the big question: what do you (want to) do with this item?
//
// do you try to lock a global: lock ^myGlobal(item) ?
// do you try to open (a possible locked) Object: set obj=##class(some.class).%OpenId(item [,4]) ?
// something else?

May I ask, which Cache or IRIS version you use?

USER>write $zv
IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST
USER>write ##class(%DynamicAbstractObject).%FromJSON("[0.1, .2]")

<THROW>%FromJSON+38^%Library.DynamicAbstractObject.1 *%Exception.General Parsing error 3 Line 1 Offset 7
USER 2e1>

As you see, my IRIS is by far not so forgiving... ☹

I'm not aware of any $ISJSON() or similar named function but you can easily make your own

ClassMethod IsJSON(str)
{
	try { ret:{}.%FromJSON(str) 1 } catch e { ret:e.Code=3 0 throw e }
}

set a="{name:""John Doe"" "
set b="{""name"":""John Doe"" }"

write ##class(some.class).IsJSON(a) --> 0
write ##class(some.class).IsJSON(b) --> 1
write ##class(some.class).IsJSON()  --> <UNDEFINED>zIsJSON+1^...  *str

As an addition to the above possibilities, you can  ALWAYS use (assumed, you have the rights to access the database where the global lives - this is true for both of the above too) extended global access.

Just one remark, this kind of access is possible but not the recommended way for creating regular classes (databases) rather to occasionally (on demand) access data from other namespace and/or namespaces.

set nsp = "USER"
set myvar = ^|nsp|MyGlobal(1,2,3)

If there is no namespace associated with the database (where the global lives) just use the implicit namespace

set dir = "^^C:\mydatabases\myfolder\"    // win-world, "^^pathToTheDatabase"
set dir = "^^/opt/mydatabases/myfolder/"  // unix-world, 
write ^|dir|myglobal(1,2,3)

// and, of course all other commands and functions
kill ^|dir|myglobal(1)
write $order(^|dir|myglobal(1,2))
set isDef = $data(^|dir|myglobal)

Indirection is your friend, which of course, works for globals, locals and other things too.

set ^myglobal="This is the root node"
set ^myglobal(12)="A node on level 1"
set ^myglobal(17)="More level 1 node"
set ^myglobal(17,1)="Data on the second level"
set ^myglobal(19,3)="More data on the second level"

; now start to play indirection
set someVariable="^myglobal" // the better way were: set someVariable=$name(^myglobal)

; and get the content of the above global
write @someVariable // --> This is the root node
write @someVariable@(12) // --> A node on level 1
write @someVariable@(17,1) // --> Data on the second level

; the same as above
set myVariable="^myglobal(17)" // Better: set myVariable=$name(^myglobal(17))
write @myVariable // --> More level 1 node
write @myVariable@(1) // Data on second level

; a bit overcomplicated
set string1="^"
set string2="myglobal"

write @(string1_string2_"("_(15+4_","_(1+2))_")") // --> More data on second level

So just (re)read the docu about indirection...

Somehow I don't understand your problem. If I create a simple class with a %Text property, insert some text data and then query for the word "time" I get it without problem.

Class DC.TextData Extends %Persistent
{
Property Comment As %Text(LANGUAGECLASS = "%Text.English", MAXLEN = 1000);
}

Now some data and the query

USER>d ##class(DC.TextData).%KillExtentData()

USER>s tt=##class(DC.TextData).%New(),tt.Comment="Notice, time is money." w tt.%Save()
1
USER>s tt=##class(DC.TextData).%New(),tt.Comment="It's time for a break." w tt.%Save()
1
USER>s tt=##class(DC.TextData).%New(),tt.Comment="There is no business like show business." w tt.%Save()
1
USER>d $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter <command>, 'q' to quit, '?' for help.
[SQL]USER>>select $zversion
5.      select $zversion

Expression_1
IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST

1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0006s/4/140/0ms
          execute time(s)/globals/cmds/disk: 0.0008s/0/394/0ms
                          cached query class: %sqlcq.USER.cls7
---------------------------------------------------------------------------
[SQL]USER>>select * from DC.TextData where Comment %CONTAINS('time')
6.      select * from DC.TextData where Comment %CONTAINS('time')

ID      Comment
1       Notice, time is money.
2       It's time for a break.

2 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0005s/4/140/0ms
          execute time(s)/globals/cmds/disk: 0.0043s/30/4,322/0ms
                          cached query class: %sqlcq.USER.cls2
---------------------------------------------------------------------------
[SQL]USER>>quit

USER>

The error message <METHOD NOT SUPPORTED> says everything.

USER>s obj={"name":"john", "value":65}

USER>w obj.%ToJSON() // --> {"name":"john","value":65}

USER>s obj={"name":"john", "value":65, "dynObj":{"info":"something"} }

USER>w obj.%ToJSON() // --> {"name":"john","value":65,"dynObj":{"info":"something"}}

USER>s obj={"name":"john", "value":65, "dynObj":{"info":"something"}, "cosObj":(##class(%Net.HttpRequest).%New()) }

USER>w obj.%ToJSON() // --> <METHOD NOT SUPPORTED> *%ToJSON,%Net.HttpRequest

So the problem is, you try to "stringify" (JS-speech) a dynamic object, where one of the properties contains a non-dynamic object (%Net.HttpRequest in the above example).

There are no reserved words for %Contains but there is a requirement on the field (property) type. Excuse me for my arrogance, but instead of reading the documentation everybody thinks, it's cheaper to ask a question in the developer community, to get a ready made solution. So please read this first and if something is not clear, then tell it us what you do not understand and we will help you to understand.

Maybe there are tools to dump a dynamic object, I don't know, but you can always write your own short method

/// obj: a dynamic object
/// opt: an option, how to display array elements
///      0: just show the index number
///      1: show the parenthesized index
///      2: show index number in a %Get() method
/// tag: optional, the name of the <obj> variable
/// 
ClassMethod DumpJSON(obj, opt = 2, tag = "")
{
	set itr=obj.%GetIterator(), arr=obj.%IsA("%DynamicArray") set:tag]"" tag=tag_"."
	while itr.%GetNext(.key, .val) {
		set:arr key=$case(opt, 2:"%Get("_key_")", 1:"("_key_")", :key)
		if $isobject(val), val.%IsA("%DynamicAbstractObject") {
			do ..DumpJSON(val,opt,tag_key)
		} else { write tag,key,": ",val,! }
	}
}

For ease of use, put it into the %ZLANGC00.mac, for example

%ZLANGC00   ; Command extensions

// Dump a dymanmic object
ZDDUMP(obj,opt,tag) Public { do ##class(your.utility).DumpJSON(obj,.opt,.tag) }

For the data, you mentioned above, the following will be displayed

set metaDynObj={"versionId":"2","security":[{"system":"http://terminol....}

zddump metaDynObj::"metaDynObj"
metaDynObj.versionId: 2
metaDynObj.security.%Get(0).system: http://terminology.hl7.org/CodeSystem/v3-Confidentiality
metaDynObj.security.%Get(0).code: U
metaDynObj.security.%Get(0).display: unrestricted

zddump [11,22,33]
0: 11
1: 22
2: 33

zddump [11,22,33]:1:"x"
x.(0): 11
x.(1): 22
x.(2): 33