eXept Software AG Logo

Smalltalk/X Webserver

Documentation of class 'JSONReader':

Home

Documentation
www.exept.de
Everywhere
for:
[back]

Class: JSONReader


Inheritance:

   Object
   |
   +--Stream
      |
      +--PeekableStream
         |
         +--JSONReader

Package:
stx:goodies/communication
Category:
Net-Communication-JSON
Version:
rev: 1.81 date: 2024/04/22 17:04:03
user: stefan
file: JSONReader.st directory: goodies/communication
module: stx stc-classLibrary: communication

Description:


I read data structures (currently build of OrderedCollection and Dictionary)
from and to JSON (Java Script Object Notation). 
Writing is done with the #toJSON: class-method (note: it will behave badly with circular data structures).
Reading via the #fromJSON: class-method.

Notice: 
    By default (and standard), 
    Date, Time and Timestamps are written as iso8601 strings,
    and read initially as strings (thus, they must be converted explicitely back if required).

    The non-standard (but supported by some browsers) format is 'new Date(...)' which is generated
    optionally, if the useISODateFormat flag is turned off in the JSONPrinter.
    However, we cannot read back this format.
    As per ECMA,
        JSON.stringify({'now': new Date()}) 
    should generate
        {''now'':''2013-10-21T13:28:06.419Z''}
    which is exactly what JSONPrinter now generates by default
        JSONPrinter encode:(Structure with: #now -> (Timestamp now)).

Another Note:
    Can now also encode arbitrary Smalltalk objects; however, the reader will always decode them 
    as dictionaries:
        JSONPrinter encode:(Point x:10 y:20).
        JSONReader decode:(JSONPrinter encode:(Point x:10 y:20)).

Final Notice:
    take a look at the other (alternative) JSON framework (Json),
    which can also be used to handle JSON data, and offers a more flexible
    object mapping mechanism to decode into real objects.

Extensability:
    allows for special encodings to be hooked in by passing a 
    dictionary mapping keywords to handler blocks.
    The block is called if a hash followed by the keyword is scanned.
    Can be used to add special encodings for private communications
    (be aware that extensions are NOT conforming any standard, and are to
     be used ONLY for private protocols)

Author:
    Public Domain Code from Robin Redeker published in lists.gnu.org
    adapted to ST/X by Claus Gittinger

copyright

COPYRIGHT (c) 2007 by eXept Software AG All Rights Reserved This software is furnished under a license and may be used only in accordance with the terms of that license and with the inclusion of the above copyright notice. This software may not be provided or otherwise made available to, or used by, any other person. No title to or ownership of the software is hereby transferred.

Class protocol:

API - decoding
o  decode: stringOrStream
decode the JSON string (as received from JavaScript) to an object

o  decode: stringOrStream extensions: extensionsOrNil
decode the JSON string (as received from JavaScript) to an object

o  decode: stringOrStream typeInfoFormat: formatSymbolOrNilOrDecoder
decode the JSON string (as received from JavaScript) to an object.
The formatSymbolOrNilOrDecoder argument can be either a symbol for one of the standard decoders
(nil, #stx or #stx2), or an instantiated decoder instance provided by the caller

o  decode: stringOrStream typeInfoFormat: formatSymbolOrNilOrDecoder verifyEOF: verifyEOF
decode the JSON string (as received from JavaScript) to an object.
Rhe formatSymbolOrNilOrDecoder argument can be either a symbol for one of the standard decoders
(nil, #stx or #stx2), or an instantiated decoder instance provided by the caller

o  decode: stringOrStream typeInfoFormat: formatSymbolOrNilOrDecoder verifyEOF: verifyEOF extensions: extensionsOrNil
decode the JSON string (as received from JavaScript) to an object.
The formatSymbolOrNilOrDecoder argument can be either a symbol for one of the standard decoders
(nil, #stx or #stx2), or an instantiated decoder instance provided by the caller

o  decode: stringOrStream typeInfoFormat: formatSymbolOrNilOrDecoder verifyEOF: verifyEOF extensions: extensionsOrNil classNamespace: classNameSpaceToUse
decode the JSON string (as received from JavaScript) to an object.
The formatSymbolOrNilOrDecoder argument can be either a symbol for one of the standard decoders
(nil, #stx or #stx2), or an instantiated decoder instance provided by the caller.
If the type is non-nil, an optional namespace may be passed to the
decoder, telling it where to look for classes being loaded
(i.e. the @type slots can then decode class names from private namespaces

o  defaultJSON5

o  defaultJSON5: aBoolean

o  fromJSON5: stringOrStream
decode the JSON5 string (as received from JavaScript) to an object.
Same as decode:
Added to make the decoder protocol-conform to other encoders.

o  fromJSON: stringOrStream
decode the JSON string (as received from JavaScript) to an object.
Same as decode:
Added to make the decoder protocol-conform to other encoders.

o  fromJSON: stringOrStream extensions: extensions
decode the JSON string (as received from JavaScript) to an object.
Same as decode:
Added to make the decoder protocol-conform to other encoders.

o  readFrom: stringOrStream
decode the JSON string (as received from JavaScript) to an object.
Same as decode:
Added to make the decoder protocol-conform to other encoders.

API - encoding
o  encode: anObject
return a JSON string which represents the object (can be sent to JavaScript).
Same as toJSON:
Added to make this more protocol-conform to other encoders.

Usage example(s):

     |s1 s2|
     s1 := self encode:#('hello' 123 true (1 'two' 3.0)).
     s2 := self decode:s1  

     |s1 s2|
     s1 := self encode:#[10 20 30]. 
     s2 := self decode:s1  

     |s1 s2|
     s1 := self encode:#('aaa' 'bbb' 'ccc'). 
     s2 := self decode:s1  

o  toJSON: anObject
return a JSON string which represents the object (can be sent to JavaScript).
Same as encode:
Added to make this more protocol-conform to other encoders.

Usage example(s):

     |s1 s2|
     s1 := self toJSON:#('hello' 123 true (1 'two' 3.0)).
     s2 := self fromJSON:s1  

Usage example(s):

     |s1 s2|
     s1 := self toJSON:(Dictionary withKeys:#('a' 'b' 'c') andValues:#(1 2 3)).
     s2 := self fromJSON:s1  

o  toJSON: anObject on: aStream
append a JSON string which represents the object (can be sent to JavaScript)
to aStream.
Same as encode:
Added to make this more protocol-conform to other encoders.

Usage example(s):

     self toJSON:#('hello' 123 true (1 'two' 3.0)) on:Transcript.

o  toJSON: anObject typeInfoFormat: formatSymbolOrNil on: aStream
append a JSON string which represents the object (can be sent to JavaScript)
to aStream.
Same as encode:
Added to make this more protocol-conform to other encoders.

Usage example(s):

     self toJSON:#('hello' 123 true (1 'two' 3.0)) on:Transcript.

instance creation
o  new
return an initialized instance

o  on: aStream


Instance protocol:

accessing
o  extensions: extensionsDictionary
Modified (format): / 20-12-2021 / 09:42:06 / cg

initialization
o  allowJSON5: aBoolean
enable/disable JSON5 features

o  classNamespace: aNamespace

o  initialize
JSONPrinter encode:((10@20) corner:(100@200))

o  makeSlotDecoderFor: typeInfoFormatOrDecoder
the argument can be either a symbol for one of the standard decoders
(nil, #stx or #stx2), or an instantiated decoder instance provided by the caller

o  typeInfoFormat: formatSymbolOrNil
specify the format of the type info to be store with the slots.
For now, the formats supported are:
nil (default): no type info (standard JSON)
#stx : encode as { '@type': , <classNameString> , 'value': <encoding> }
#stx2 : encode as { '@type': , <classNameString> , slots... }
more formats may be implemented in the future.

o  typeSlotName: aString

private
o  atEnd
(comment from inherited method)
return true if the end of the stream has been reached;
- we do not know here how to do it, it must be redefined in subclass

o  next
I'm returning the next non-whitespace character

o  nextExtension
#key ... to call extension 'key' passing the input stream which is positioned
at the character after key

o  nextJSONArray
I decode JSON arrays from self and will return an Array for them.

o  nextJSONDict
I decode JSON objects from self and will return a Dictionary containing all the key/value pairs.

o  nextJSONNumber
I'm extracting a number in JSON format from self and return Integer or Float depending on the input.
Accepts NaN, +Inf and -Inf.
If the allowJSON5 flag is true, also allow base2 and base16 numbers (0bXXXX and 0xXXXX)

o  nextJSONNumber: peekC
I'm extracting a number in JSON format from self and return Integer or Float depending on the input.
Accepts NaN, +Inf and -Inf.
If the allowJSON5 flag is true, also allow base2 and base16 numbers (0bXXXX and 0xXXXX)

o  nextJSONString
I'm extracting a JSON string from self and return it as String.

o  nextJSONString: quote
I'm extracting a JSON string from my stream and return it as String.
The quote character is assumed to be last read.

o  peek
I'm peeking for the next non-whitespace character
and will drop all whitespace in front of it and (for JSON5) all C-style comments.
Don't call me when reading the contents of a string

o  stream: aStream

reading
o  decode: stringOrStream

o  nextJSONObject
I decode a json self to a value, which will be one of:
nil, true, false, OrderedCollection, Dictionary, String or Number
(I will return Integer or Float depending on the input).
Somewhat picky on the input (expects 'null', 'true', 'false' to be written as that


Examples:


    |o1 s o2|
    o1 := OrderedDictionary new.
    o1['a'] := 1.
    o1['b'] := true.
    s := JSONPrinter toJSON:o1.   
    o2 := JSONReader fromJSON:s.   
    self assert:(o1 sameContentsAs: o2).  
    |o1 s o2|
    o1 := OrderedDictionary new.
    o1['aaa'] := 'AAA'.
    o1['bbb'] := 123.
    o1['ccc'] := 1.
    s := JSONPrinter toJSON:o1.   
    o2 := JSONReader fromJSON:s.   
    self assert:(o1 = o2).  
    |o1 s o2|
    o1 := #('hello' 123 nil true false (1 'two' 3.0) -4 -1.5).
    s := JSONPrinter toJSON:o1.   
    o2 := JSONReader fromJSON:s.   
    self assert:(o1 = o2).  
    |o1 s o2|
    o1 := InlineObject slotNames:#('foo' 'bar' 'baz') values:#(1 2 3).
    s := JSONPrinter toJSON:o1.  
    o2 := JSONReader fromJSON:s.    
    |o1 s o2|
    o1 := OrderedDictionary withKeys:#('foo' 'bar' 'baz') andValues:#(1 2 3).
    s := JSONPrinter toJSON:o1.  
    o2 := JSONReader fromJSON:s.   
    self assert:(o1 = o2).
    |o1 s o2|
    o1 := Dictionary withKeysAndValues:#('one' 1 'two' 2 'three' 3.0 'four' 'vier').
    s := JSONPrinter toJSON:o1.
    o2 := JSONReader fromJSON:s.   
    self assert:(o1 sameContentsAs: o2).
    |o1 s o2|
    o1 := Dictionary withKeysAndValues:#('one two three' 123).
    s := JSONPrinter toJSON:o1.
    o2 := JSONReader fromJSON:s.   
    self assert:(o1 sameContentsAs: o2).
    |o1 s o2|
    o1 := { Date today . Time now . Timestamp now }.
    s := JSONPrinter encode:o1.
    o2 := JSONReader decode:s.
    -- notice: data & times are decoded as strings
    |o1 s o2|
    o1 := { Point x:10 y:20 }.
    s := JSONPrinter new useISODateFormat:false; encode:o1.
    o2 := JSONReader decode:s.
    self assert:(o1 sameContentsAs: o2).
    |o1 s o2|
    o1 := { Date today . Time now . Timestamp now }.
    s := JSONPrinter new useISODateFormat:false; encode:o1.
    o2 := JSONReader decode:s -- fails as non-JSON  
notice: byteArray encoded as array - will decode as array
    |o1 s o2|
    o1 := #[1 2 3].
    s := JSONPrinter toJSON:o1.  
    o2 := JSONReader fromJSON:s  
using extensions
    |ext o|

    ext := Dictionary new.
    ext at:#foo put:[:in | in next. Integer readFrom:in. ].
    o := JSONReader fromJSON:'{ ',Character doubleQuote,'bla',Character doubleQuote,': #foo:1234 }' extensions:ext. 
    self assert:(o bla == 1234).


ST/X 7.7.0.0; WebServer 1.702 at 20f6060372b9.unknown:8081; Mon, 30 Dec 2024 17:37:23 GMT