Representation of JSON data as Event pulled from an Iterator.

StreamParser produces events according to JSON data read from a Tokenizer.

errorReporting adapts an Iterator so that exceptions thrown while iterating are returned from the iterator, rather than propagating from it.

streamToVisitor() provides a bridge between producers of events (in the form of Iterators) and consumers of those events (in the form of a Visitor.

StreamingVisitor provides a bridge in the reverse direction. It can be used to produce events by visiting a Value.

Aliases
EventSource Codeshared Event=> ObjectStartEvent|ObjectEndEvent|ArrayStartEvent|ArrayEndEvent|KeyEvent|String|Float|Integer|Boolean|Null

An event encountered when processing JSON data

Values
arrayEndSource Codeshared arrayEnd arrayEnd

The end of the current array encountered when processing JSON data

arrayStartSource Codeshared arrayStart arrayStart

The start of an array encountered when processing JSON data

objectEndSource Codeshared objectEnd objectEnd

The end of the current object encountered when processing JSON data

objectStartSource Codeshared objectStart objectStart

The start of an object encountered when processing JSON data

Functions
streamToVisitorSource Codeshared void streamToVisitor(Iterator<Event> stream, Visitor visitor)

Calls a visitor according to the events obtained from a stream.

Interfaces
NestingEventSource Codeshared NestingEvent<Self,out Other>
given Self satisfies NestingEvent<Self,Other>
given Other satisfies NestingEvent<Other,Self>

An Event that has a corresponding other event

Classes
ArrayEndEventSource Codeshared abstract ArrayEndEvent

The end of a JSON array, emitted when ] is parsed.

ArrayStartEventSource Codeshared abstract ArrayStartEvent

The start of a JSON array, emitted when [ is parsed.

KeyEventSource Codeshared KeyEvent

A key encountered when processing JSON data

LookAheadSource Codeshared LookAhead<T>

A look-ahead buffer wrapping a stream

ObjectEndEventSource Codeshared abstract ObjectEndEvent

The end of a JSON object/hash, emitted when } is parsed.

ObjectStartEventSource Codeshared abstract ObjectStartEvent

The start of a JSON object/hash, emitted when { is parsed.

StreamParserSource Codeshared StreamParser

A parser for JSON data as specified by RFC 7159 which produces a stream of Event to be handled by the caller. The parser produces events as it reads the source StreamParser.input, so it's possible to start parsing JSON while it's still being received.

This parser does not enforce uniqueness of keys within JSON objects. It is usually not onerous for the caller to do so if they require such enforcement.

By default ParseExceptions will propagate out of calls to StreamParser.next() when a error is detected. You can use errorReporting to report errors as Exceptions within the stream.

Example

Suppose we have the domain model:

class Order(address, items) {
    shared String address;
    shared Item[] items;
}
class Item(sku, quantity) {
    shared String sku;
    shared Integer quantity;
}

And we want to parse JSON that looks like this:

    {
      "address":"",
      "items":[
        {
          "sku":"123-456-789",
          "quantity":4
        },
        {
          "sku":"456-789",
          "quantity":20
        }
      ]
    }

Then we might write a parser like this:

 class OrderParser() {

     late variable LookAhead<Event> stream;

     String missingKey(String container, String key) {
         return "``container``: '``key``' missing at line ``stream.line``'";
     }
     String duplicateKey(String container, String key) {
         return "``container``: '``key``' occurs more than once at line ``stream.line``'";
     }
     String keyType(String container, String key, String expectedType) {
         return "``container``: '``key``' key is supposed to be of ``expectedType`` type at line ``stream.line``";
     }
     String unexpectedKey(String container, String key) {
         return "``container``: '``key``' key not supported at line ``stream.line``";
     }
     String unexpectedEvent(String container, Event|Finished event) {
         return "``container``: unexpected event ``event else "null"`` at line ``stream.line``";
     }

     "Parses an item from events read from the given parser.
      Returns the item or an error explanation."
     Item|String parseItem() {
         if (!(stream.next() is ObjectStartEvent)) {
             return "Item: should be a JSON object";
         }
         variable String? sku = null;
         variable Integer? quantity = null;
         while(true) {
             switch(event=stream.next())
             case (is KeyEvent) {
                 switch(key=event.key) 
                 case ("sku") {
                     if (is String s = stream.next()) {
                         if (sku exists) {
                             return duplicateKey("Item", "sku");
                         }
                         sku = s;
                     } else {
                         return keyType("Item", "sku", "String");
                     }
                 }
                 case ("quantity") {
                     if (is Integer s = stream.next()) {
                         if (quantity exists) {
                             return duplicateKey("Item", "quantity");
                         }
                         quantity = s;
                     } else {
                         return keyType("Item", "sku", "Integer");
                     }
                 }
                 else {
                     return unexpectedKey("Item", key);
                 }
             }
             case (is ObjectEndEvent) {
                 if (exists s=sku) {
                     if (exists q=quantity) {
                         return Item(s, q);
                     }
                     return missingKey("Item", "quantity");
                 }
                 return missingKey("Item", "sku");
             }
             else {
                 return unexpectedEvent("Item", event);
             }
         }
     }

     "Parses an order from events read from the given parser.
      Returns the order or an error explanation."
     Order|String parseOrder() {
         if (!(stream.next() is ObjectStartEvent)) {
             return "Order: should be a JSON object";
         }
         variable String? address = null;
         value items = ArrayList<Item>();
         while(true) {
             switch(event=stream.next())
             case (is KeyEvent) {
                 switch(key=event.key) 
                 case ("address") {
                     if (is String s = stream.next()) {
                         if (address exists) {
                             return duplicateKey("Order", "address");
                         }
                         address = s;
                     } else {
                         return keyType("Order", "address", "String");
                     }
                 }
                 case ("items") {
                     if (!items.empty) {
                         return duplicateKey("Order", "items");
                     }
                     if (!stream.next() is ArrayStartEvent) {
                         return keyType("Order", "items", "Array");
                     }
                     while (stream.peek() is ObjectStartEvent) {
                         switch (item=parseItem())
                         case (is String) {
                             return item;
                         }
                         case (is Item) {
                             items.add(item);
                         }
                     }
                     assert(stream.next() is ArrayEndEvent);
                 }
                 else {
                     return unexpectedKey("Order", key);
                 }
             }
             case (is ObjectEndEvent) {
                 if (exists a=address) {
                     return Order(a, items.sequence());
                 }
                 return missingKey("Order", "address");
             }
             else {
                 return unexpectedEvent("Item", event);
             }
         }
     }

     shared Order|String parse(String json) {
         stream = LookAhead(StreamParser(StringTokenizer(json)));
         return parseOrder();
     }
 }

While this is certainly verbose it's extremely readable and regular.

StreamingVisitorSource Codeshared StreamingVisitor

Produces a stream of events from the descendents of the given root value.

arrayEndSource Codeshared arrayEnd

The end of the current array encountered when processing JSON data

arrayStartSource Codeshared arrayStart

The start of an array encountered when processing JSON data

objectEndSource Codeshared objectEnd

The end of the current object encountered when processing JSON data

objectStartSource Codeshared objectStart

The start of an object encountered when processing JSON data