WRITE-JSON options

Posted by jmls on 03-May-2011 06:22

I need to pass a json string through webspeed, and there's a wrinkle.

If I use write-json on a Customer temp-table (sports database), I get the following json code:


{"customers": [

..snip..

  {
    "Cust-Num": 72,
    "Country": "USA",
    "Name": "Birdy's Badminton",
    "Address": "125 Federal St",
    "Address2": "",
    "City": "Hydro",
    "State": "OK",
    "Postal-Code": "73048",
    "Contact": "Orrin Meagher",
    "Phone": "(405) 233-0881",
    "Sales-Rep": "JAL",
    "Credit-Limit": 52900.00,
    "Balance": 28442.00,
    "Terms": "Net30",
    "Discount": 45,
    "Comments": "Speak to Debbie before shipping any products."
  },
  {
    "Cust-Num": 73,
    "Country": "Austria",
    "Name": "Auffi Bergausrustung",
    "Address": "Tiroler Landesstr. 5",
    "Address2": "",
    "City": "Innsbruck",
    "State": "West",
    "Postal-Code": "A-6020",
    "Contact": "Fr. Abseiler",
    "Phone": "0512\/31644",
    "Sales-Rep": "GPE",
    "Credit-Limit": 24800.00,
    "Balance": 17673.00,
    "Terms": "Net30",
    "Discount": 25,
    "Comments": ""
  },

..snip..

]}

however, the required json output is thus: (notice the extra "total: 80" field)

{total: 80,
     "customers": [

..snip..

  {
    "Cust-Num": 72,
    "Country": "USA",
    "Name": "Birdy's Badminton",
    "Address": "125 Federal St",
    "Address2": "",
    "City": "Hydro",
    "State": "OK",
    "Postal-Code": "73048",
    "Contact": "Orrin Meagher",
    "Phone": "(405) 233-0881",
    "Sales-Rep": "JAL",
    "Credit-Limit": 52900.00,
    "Balance": 28442.00,
    "Terms": "Net30",
    "Discount": 45,
    "Comments": "Speak to Debbie before shipping any products."
  },
  {
    "Cust-Num": 73,
    "Country": "Austria",
    "Name": "Auffi Bergausrustung",
    "Address": "Tiroler Landesstr. 5",
    "Address2": "",
    "City": "Innsbruck",
    "State": "West",
    "Postal-Code": "A-6020",
    "Contact": "Fr. Abseiler",
    "Phone": "0512\/31644",
    "Sales-Rep": "GPE",
    "Credit-Limit": 24800.00,
    "Balance": 17673.00,
    "Terms": "Net30",
    "Discount": 25,
    "Comments": ""
  },

..snip..
]}





So, are there any options on WRITE-JSON to add this field in ?

if not, I see two options

1) write my own json, Urgh. I may have numerous tables, so will need to write a code generator (again)

2) cheat: write-json to a longchar, then do

ASSIGN substring(MyLongchar,2,0) = 'total: 80,~n'.
any other options ?

All Replies

Posted by Peter Judge on 03-May-2011 07:38

jmls wrote:

So, are there any options on WRITE-JSON to add this field in ?

if not, I see two options

1) write my own json, Urgh. I may have numerous tables, so will need to write a code generator (again)

2) cheat: write-json to a longchar, then do

ASSIGN substring(MyLongchar,2,0) = 'total: 80,~n'.


any other options ?



Sorry, no, none that I know of .... oh wait, Marian Edu has something that's Windows-only (AFAICT) at http://www.ganimede.ro/cms/2011/01/abljson-release-v1-1/ .

I've written my own when needed (not a generic JSON serialization though, which is something I'd love to add/have added to the AutoEdge components ).

-- peter

Posted by Admin on 03-May-2011 08:04

Well, thanks for that Peter... one of the reasons I did that was to have full control on how the data is being serialized, this will never be implemented in write-json and for a good reason i think. Each has his own serialization needs and 'polluting' the write-xml with all those possible variations will make it very hard to use (for mere mortals).

The code should work with any Unix flavor (I think), only tested on Windows and Linux though

Posted by mresnick on 03-May-2011 08:28

Julian,

There is a JSON Parser feature planned for OpenEdge 11.0. It's similar in concept to the DOM Parser, at least at a very high level.

With it you'd be able to accomplish what you're asking for with the following:

joCustomers = NEW JsonObject().       /* 1 */

joCustomers:Read(hCustomers).         /* 2 */

joCustomers:Add("total", 80).         /* 3 */

joCustomers:WriteStream("WebStream"). /* 4 */

Line 2 loads the contents of the temp-table into a series of in-memory constructs representing the JSON objects and JSON arrays that you included in your original messge. Line 4 serializes the contents of those constructs directly out to the HTTP response.

If you're interested in experimenting with this planned feature, contact product management about getting involved in the OpenEdge 11.0 Beta program.

Michael

[Additional text to clarify the example based on confusion expressed in a later reply]

The code above is not, in fact pseudo code. This is working code in the OpenEdge 11 internal development environment.

The JsonObject ABL class represents a JSON (or JavaScript) object that can be addressed using named properties, such as "Customers" or "total".

Line 1 creats an empty object.

As mentioned above, Line 2 loads the contents of the Customers temp-table into the JsonObject. A JsonObject has a list of properties addressable by names. There are also JsonArray, addressable by numeric indexes.

The JsonObject has one property called "Customers". The "Customers" property represents the contents of the temp-table. The contents of the temp-table been parsed into an ABL object that acts like an array. In the original example that appeared as "[{...},{...}]".

At this point there are ABL objects in memory representing the following:

{              /* This is what joCustumers points to */

  "Customers":
  [            /* This is represented by a JsonArray object */

      {...},   /* This is item #1: a JsonObject object containing individual values "Cust-num",
                                   Country", etc. */

      {...}    /* This is item #2: a JsonObject object */
  ]

}

Line 3 alters the joCustomers ABL object. The contents of the object now looks like:

{              /* This is what joCustumers points to */
  "Customers":
  [            /* This is represented by a JsonArray object */

      {...},   /* This is item #1: a JsonObject object containing individual values "Cust-num",
                                    "Country", etc. */

      {...}    /* This is item #2: a JsonObject object */
  ],
  "total": 80    /* New JSON number property */
}

The last line, in object oriented fashion is a direction to the object to write itself out to the WebStream. That object, in turn tells each of its contained objects to write themselves out. The end result is JSON text that looks like the preceding example.

One more quick example: if you wanted to get the country of the the second customer record ("Australia" in the example) one could do the following:

/*         Get the list of entries   2nd element      Country property      */

joCustomer:GetJsonArray("Customers"):GetJsonObject(2):GetCharacter("Country").

Message was edited by: Michael Resnick

Posted by Admin on 03-May-2011 11:12

could be just pseudo-code but having a writer object with a read method does look a little confusing for some tired minds like mine

anyway, in case you can wait for the new major OE release the writer functionality you can find there... http://www.ganimede.ro/help/abljson/files/Writer-cls.html

Posted by cwills on 29-May-2011 21:43

... Or another alternative. See Attachment.

A lightweight JSON writer, useful for classes that need to serialize themselves to JSON. Supports method chaining:

DEFINE VARIABLE writer AS JsonWriter NO-UNDO.

writer = (NEW JsonWriter())

    :Pair("firstName","John")

    :Pair("lastName","Smith")

    :Pair("age", 25)

    :StartObject("address")

        :Pair("streetAddress", "21 2nd Street")

        :Pair("city", "New York")

        :Pair("state", "NY")

        :Pair("postalCode", "10021")

    :EndObject()

    :StartArray("phoneNumber")

        :StartObject()

            :Pair("type", "home")

            :Pair("number", "212 555-1234")

        :EndObject()

        :StartObject()

            :Pair("type", "fax")

            :Pair("number", "646 555-4567")

        :EndObject()

    :EndArray()

:End().

NOTE: I have only done basic testing so far...

Posted by Admin on 30-May-2011 01:13

Cameron, that's great but not that lightweight when it comes of performance... all those string manipulation routines aren't the most performant things in ABL

The most performant implementation will be the PSC one as this is going to use the low-level 3GL routines although... I might 'translate' the JSON parser C implementation in ABL if I find some time to spare

Posted by Admin on 30-May-2011 01:18

Thanks Michael, somehow I've missed your update (editing a message does show the thread as being updated???)... anyway now it's more clear, any plans for a lightweight SAX approach or the DOM Json object is enough for now?

And, when you are reffering to contained objects... those are low-level objects (handles in our world) not regular ABL objects right?

Posted by mresnick on 31-May-2011 07:42

"It's on our roadmap, but not planned for a specific release"

Though nothing has been planned post 11.0 as far as I know.

Michael

Posted by mresnick on 02-Jun-2011 13:48

I'll repeat my previous reply (which was incomplete) and add to it.

Thanks Michael, somehow I've missed your update (editing a message does show the thread as being updated???)...

Apparently not. Hence the new reply, rather than an edit of my previous reply.

Anyway now it's more clear, any plans for a lightweight SAX approach or the DOM Json object is enough for now?

"It's on our roadmap, but not planned for a specific release"

Though nothing has been planned post 11.0 as far as I know.

And, when you are reffering to contained objects... those are low-level objects (handles in our world) not regular ABL objects right?

I'm talking about ABL OO objects. They're in the Progress.Json.ObjectModel package. Since the point of the API was to mimic JavaScript objects, it made sense to use ABL objects.

This thread is closed