What is the best way to serialize object/list/array/etc to string?

Hello!

Are there any short ways of serializing COS entity to the string? I am searching for something like .$toJSON() , but I need Caché 2013.* support.

The one way I supposed to work is to use $listbuild:

 

set str = $LISTTOSTRING($LB(1,2,3,",",5))
set list = $LISTFROMSTRING(str,",")
zw list
list=$lb("1","2","3","","","5")

 

But the problem is that $listbuild does not escape delimiters, and it is not possible to deserialize serialized string. The structure I need to serialize may contain any characters.

Are there any short ways to do this in Caché to avoid writing custom parser?

  • + 1
  • 0
  • 1509
  • 12
  • 3

Answers

ZWRITE command is implemented in the ObjectScript, and if you are happy with the way it's quoting $LB then you could reuse it's core functionality, i.e.

DEVLATEST:22:47:39:USER>set lb = $listbuild(1,2,3,",",5)
 
DEVLATEST:22:47:41:USER>write $$Quote^%qcr(lb)
$lb(1,2,3,",",5)

 

Nice, but how to deserialize this string? Are there any built-in methods to do so?

Given the returned from Quote^%qcr expression you could use XECUTE to reevaluate the string, i.e.:

DEVLATEST:22:51:39:USER>set q= $$Quote^%qcr(lb)
 
DEVLATEST:22:51:54:USER>x "s u = "_q
 
DEVLATEST:22:52:30:USER>zw u
u=$lb(1,2,3,",",5)

Thanks! But if I have possibility to avoid using this method - I'll use one :)

You can use %SerialObject for that:

Class Utils.Serial Extends %SerialObject
{

Property Payload As %String;

/// zw ##class(Utils.Serial).Test()
ClassMethod Test(input As %String = {$lb(1,2,3,",",5)}) As %String
{
    
    set obj = ##class(Utils.Serial).%New()
    set obj.Payload = input
    do obj.%SerializeObject(.str)
    kill (str)
    
    set obj = ##class(Utils.Serial).%Open(str)
    return obj.Payload
}
}

For example:

zw ##class(Utils.Serial).Test("1,2,3,,,5")
>"1,2,3,,,5"

zw ##class(Utils.Serial).Test($lb(1,2,3,",",5))
>$lb(1,2,3,",",5)

Hi

you can take a look at :

%ZEN.Auxiliary.jsonProvider

 

This provider can give you a JSON object that allows you to serialize.

Browse the class above via class reference and have a look at the methods bellow.

http://docs.intersystems.com/cache201317/csp/documatic/%25CSP.Documatic.cls

%WriteJSONFromObject

%ObjectToJSON

 

I hope it helps

Thank you Fabio! The problem with all of the methods in %ZEN.Auxiliary.jsonProvider is that it writes JSON string directly to the current device, not allowing to get the string itself. But nevertheless I did it by rewriting this method and creating a custom method, which returns a string instead of printing JSON to the device.

Comments

Even after looking at Timur and Eduard's answers, I don't understand the question. $lb(1,2,3,",",5) returns a string. Why are you trying to round trip it through a delimited string using $lts and $lfs?

Don't understand what are $lts and $lfs. I need to convert this string back to the structure, so I can read it's elements. The delimiter needs to be escaped somehow.

$lts and $lfs are the abbreviated forms of $listtostring and $listfromstring, respectively. $lb(1,2,3,",",5) returns a string, which seems to be what you want. I don't understand why you are then converting it to a delimited string with $listtostring.

[UPD fixed] Got you. Incredible! It seems that this is what I needed:

USER>set string = $lb($C(1),$c(2),$c(3),",",$c(0))
USER>w $a($LISTGET(a,1))
1
USER>w $a($LISTGET(a,2))
2
USER>w $a($LISTGET(a,3))
3
USER>w $LISTGET(a,4)
,
USER>w $a($LISTGET(a,5))
0

Until the list values may contain any set of characters here this is the best solution, thank you, Jon!

I don't understand what you mean by "until the list values may contain any set of characters".

Nor do I understand the purpose of the second line of your terminal transcript above. None of your subsequent lines do anything with your local variable called list

Thanks John, the above terminal transcript is not correct showing what I wanted to show. I've fixed this.

Why can't you understand the purpose of this?

I can only transfer strings by some channel (f.e. interprocess communication). Now I need to send the array of strings (list, object, etc) from sender to receiver. To send this array, I must convert it to the string and then, after receiving, convert it back to array to read it's elements. How to do this in Caché 2013.*? The strings in array can contain any characters, so it is not possible to specify any delimiter character.

Finally I got why the question is pretty confusing for you. Just imagine, I didn't know that $lb() returns the simple string. I didn't know that I can write this string to file, read it from file, and then apply $LISTGET function to it. I didn't know $LISTGET function takes string type. I thought that $LIST is something more than just a string.

So the solution:

set string = $LB(1,2,3,",",5)
/* send this string anywhere */
set element = $LISTGET(string, 4) // = ","

Now it becomes clear!

I had wondered if your requirement for "string" was that it contained only printable characters. In such a case one solution could be to base64-encode the result of $listbuild. On a Unicode instance of Caché you'd need to convert it to UTF8 first.

USER>s list=$lb($c(0),$c(1),"Hello",",","World")
 
USER>s printable=$system.Encryption.Base64Encode($zconvert(list,"O","UTF8"))
 
USER>w printable
AwEAAwEBBwFIZWxsbwMBLAcBV29ybGQ=
USER>s list2=$zconvert($system.Encryption.Base64Decode(printable),"I","UTF8")
 
USER>w list2=list
1
USER>w $a($li(list2,1))
0
USER>w $a($li(list2,2))
1
USER>w $li(list2,3)
Hello
USER>w $li(list2,4)
,
USER>w $li(list2,5)
World
USER>

Note that Base64Encode adds a CRLF after every 76 characters, so if you want to remove these from your "printable" you can either $TR(printable,$c(13,10)) or on 2015.2 or later you can pass a second argument to Base64Encode.

Thanks for the tips! My use case is only to get the string, it doesn't matter printable or not.

I also tested that this works with nested lists, so everything is OK now:

USER>set l = $lb(1,2,$lb(3,4,5),6)
 
USER>w $LISTGET($LISTGET(l, 3), 2)
4
USER>w $LISTGET($LISTGET(l, 3), 1)
3
USER>w $LISTGET($LISTGET(l, 3), 3)
5
USER>w $LISTGET(l, 4)