JSON to Eiffel object

51 views
Skip to first unread message

anders

unread,
Sep 19, 2018, 3:45:49 AM9/19/18
to Eiffel Users
Hi

The https://github.com/eiffelhub/json implementations Readme file indicates that it might be implemented a JSON to Eiffel object converter in the future:

"The converters part is now obsolete and not recommended (remember: the
objective of converters were to provide two basic features Eiffel2JSON and
JSON2Eiffel). There will be a new design for converters as a standalone
library on top of Current json library."

What status has that project?

Regards

Anders

Jocelyn Fiat

unread,
Sep 19, 2018, 4:07:33 AM9/19/18
to Eiffel Users
The Eiffel JSON library is actively maintained, and used in many projects.

The "converters" interfaces are obsolete as the design based on "once functions" was complex to use safely (and in practice was dangerous), whenever you had more than one converter in the same project.
Since this JSON library is used by several libraries, it was not working as expected as each time a conversion had to be done, it was required to configure again and again the converters (which was not done by the various libraries).

Now, a safer alternative solution is provided under the name "serialization", you can see https://github.com/eiffelhub/json/tree/master/examples/serialization for a simple example, and https://github.com/eiffelhub/json/tree/master/test/autotest/test_suite/serialization for test cases.
There are mainly two interfaces:
  to convert Eiffel object to JSON string.
  to convert JSON string to Eiffel object

For convenience is provided https://github.com/eiffelhub/json/blob/master/library/serialization/json_reflector_serialization.e using the reflexion mechanism, so it could help for simple objects.

The https://github.com/eiffelhub/json/tree/master/library/serialization/with_reference cluster provides classes to support a custom notion of reference.

It is also possible to use your own custom (de)serializers for specific types, by registering them via the context interfaces, see the test cases for examples.
The documentation and the "serialization" example still need to be completed to demonstrate the various capabilities.

Regards,
-- Jocelyn





--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
Visit this group at https://groups.google.com/group/eiffel-users.
For more options, visit https://groups.google.com/d/optout.


--
Jocelyn
------------------------------------------------------------------------
Eiffel Software
https://www.eiffel.com
Customer support: https://support.eiffel.com
User group: https://groups.google.com/forum/#!forum/eiffel-users
------------------------------------------------------------------------

anders

unread,
Sep 19, 2018, 4:41:06 AM9/19/18
to Eiffel Users
Seems to something usefull for my project.

How would a simple example look like using this class:


I assume it is this feauture I should call:

from_json (a_json: detachable JSON_VALUE; ctx: JSON_DESERIALIZER_CONTEXT; a_type: detachable TYPE [detachable ANY]): detachable ANY




if the json was as simle as:

{ name: "John", city: "New York" }

Finnian Reilly

unread,
Sep 19, 2018, 5:55:22 AM9/19/18
to Eiffel Users
Support for JSON in Eiffel-Loop

the Eiffel-Loop base library of has support for simple JSON to Eiffel object conversion (i.e. not nested.) via the class EL_SETTABLE_FROM_JSON_STRING. Here are some  example implementations from the Amazon Instant Access API for Eiffel.

EL_SETTABLE_FROM_JSON_STRING*

This JSON implementation has the following features

  • Makes use of the reflection mechanism to map JSON fields to Eiffel class attributes by name.
  • Ability to map differing field naming conventions, for example camelCase (JSON) to snake_case (Eiffel). Possibilities are defined in class EL_NAMING_ROUTINES.
  • Converts JSON data to any of the Eiffel basic types and strings. The full list of possible conversions is defined by the inheritance hierarchy
EL_REFLECTED_FIELD*
  • Support for Eiffel to JSON conversion via routine: as_json

Jocelyn Fiat

unread,
Sep 19, 2018, 6:07:36 AM9/19/18
to Eiffel Users
Using JSON serialization

it could be something like

    process_json_dict_to_eiffel
        local
            fac: JSON_SERIALIZATION_FACTORY
            conv: JSON_SERIALIZATION
            json: STRING
        do
            json := "{ %"name%": %"John%", %"city%": %"New York%" }"
           
            conv := fac.smart_serialization
            conv.context.deserializer_context.set_value_creation_callback (create {JSON_DESERIALIZER_CREATION_AGENT_CALLBACK}.make (
                    agent (a_info: JSON_DESERIALIZER_CREATION_INFORMATION)
                    do
                        if a_info.static_type = {STRING_TABLE [STRING_32]} then
                            a_info.set_object (create {STRING_TABLE [STRING_32]}.make (0))
                        end
                    end
                )
            )
            if attached {STRING_TABLE [STRING_32]} conv.from_json_string (json, {STRING_TABLE [STRING_32]}) as obj then
                across
                    obj as ic
                loop
                        -- Warning: it should use the localication printer due to potential unicode value...
                    print (ic.key)
                    print ("=")
                    print (ic.item)
                    print ("%N")
                end
            end
        end

Maybe a bit complicated, but if needed, we could provide a simple deserializer that would handle everything as STRING_32, ARRAYED_LIST [STRING_32], and STRING_TABLE [STRING_32] , or eventually the "detachable" version of those, as an array or object could have json "null" value.

I will post later such deserializer.

anders

unread,
Sep 19, 2018, 6:39:05 AM9/19/18
to Eiffel Users
Yes, it seems complicated. Why did you not use the "json_reflector_deserializer" as I thought was the class to use?

anders

unread,
Sep 19, 2018, 6:41:58 AM9/19/18
to Eiffel Users
Interesting. Could you write an example for converting this JSON

{ name: "John", city: "New York" }

to a class with two attributes name and city?

Larry Rix

unread,
Sep 19, 2018, 7:23:40 AM9/19/18
to eiffel...@googlegroups.com
FYI: The "json_ext" library found here is one that I open sourced from Atlanta. It works fairly well. Not sure what the converters are about—will have to look that up.

Larry Rix
Moonshot Software
Rocket science for everyone!
Savannah, GA

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users+unsubscribe@googlegroups.com.

Finnian Reilly

unread,
Sep 19, 2018, 7:42:02 AM9/19/18
to Eiffel Users


Interesting. Could you write an example for converting this JSON

{ name: "John", city: "New York" }

to a class with two attributes name and city?

Ok, I will make a small test set to illustrate 

Jocelyn Fiat

unread,
Sep 19, 2018, 8:17:06 AM9/19/18
to Eiffel Users
Yes it sounds complex, as it was design to be adapted to any cases, not just simple cases.

The "reflector" deserializer works together with the "reflector" serializer.
it just iterates over the attributes and turns them info json value.
For basic type it is obvious, for reference, it goes recursively, and add a "$TYPE" value to keep track of the type of the referenced object.
then the deserializer relies on those pieces of information, and the extra "callback" to know how to create the expected object.

Otherwise, I remember now why we haven't provided a basic serialization from JSON to standard objects (such as HASH_TABLE and so on ...), just because we already have the JSON value which is a good representation of the json string.
Maybe we could improve the JSON_STRING... and so on with helper functions.

Also I will update soon the examples with custom serializations, i.e to target specific classes (for instance class PERSON that would have `name` and `city`)



anders

unread,
Sep 19, 2018, 8:55:42 AM9/19/18
to Eiffel Users
Hi

I tried a small example, as base I used the code snippets in the readme file https://github.com/ljr1981/json_ext

However the "metadata_refreshed" that is deferred was not described in the REAME-file so I just copied it from another example Larry helped me with. I am not sure about the impact of this feature is in this case.

Otherwise, it worked very well for my simple case and I will stick to this solution for now.


 

note
 description
: "Summary description for {TASK_DATA}."
 author
: ""
 date
: "$Date$"
 revision
: "$Revision$"


class
 TEST_DATA


inherit
 JSON_DESERIALIZABLE


create
 make_from_json


feature


 firstname
: STRING
 lastname
: STRING


convertible_features
(a_object: ANY): ARRAY [STRING]
 
-- <Precursor>
 once
 
Result := <<"firstname", "lastname">>
 
end


make_from_json
(a_json: STRING)
 
-- <Precursor>
 
require else -- This must be here because the ancestor is False.
 
True     -- Leaving it False, will cause this to fail.
 
local
 l_object
: detachable JSON_OBJECT         -- You must have one of these because ...
 l_any
: detachable ANY
 
do
 l_object
:= json_string_to_json_object (a_json) -- ... the `a_json' STRING is parsed to a JSON_OBJECT.
 check attached_object: attached l_object end   -- This proves that our JSON parsing was okay.


 firstname := json_object_to_json_string_representation_attached ("firstname", l_object)
 lastname := json_object_to_json_string_representation_attached ("lastname", l_object)
 end


 metadata_refreshed( a_current: ANY): ARRAY [JSON_METADATA]


 do
 Result := <<
 >>
 end


end


Den onsdag 19 september 2018 kl. 13:23:40 UTC+2 skrev Larry Rix:
FYI: The "json_ext" library found here is one that I open sourced from Atlanta. It works fairly well. Not sure what the converters are about—will have to look that up.

Larry Rix
Moonshot Software
Rocket science for everyone!
Savannah, GA

On Wed, Sep 19, 2018 at 3:45 AM, anders <and...@bsharp.se> wrote:
Hi

The https://github.com/eiffelhub/json implementations Readme file indicates that it might be implemented a JSON to Eiffel object converter in the future:

"The converters part is now obsolete and not recommended (remember: the
objective of converters were to provide two basic features Eiffel2JSON and
JSON2Eiffel). There will be a new design for converters as a standalone
library on top of Current json library."

What status has that project?

Regards

Anders

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.

Finnian Reilly

unread,
Sep 19, 2018, 9:40:38 AM9/19/18
to Eiffel Users
Simple example of  JSON for Eiffel-Loop 
Hi Anders

Interesting. Could you write an example for converting this JSON

{ name: "John", city: "New York" }

to a class with two attributes name and city?

To illustrate I have added to Eiffel-Loop this class PERSON and a test suite  SETTABLE_FROM_JSON_STRING_TEST_SET

BTW, the JSON you wrote is not valid JSON according to this validator 

Note that in class person it doesn't matter whether you make the type of name STRING, STRING_32 or ZSTRING, it will still work the same. I have added 2 extra fields age and gender, to demonstrate automatic type conversion.

Note also that you can optionally use the class EL_REFLECTIVE instead of EL_REFLECTIVELY_SETTABLE, the difference being that the latter initialises reference attributes for you in make_default. With the former you are responsible for initialising all fields yourself.

class
   PERSON

inherit
   EL_REFLECTIVELY_SETTABLE
      rename
         field_included as is_any_field,
         export_name as export_default,
         import_name as import_default
      end

   EL_SETTABLE_FROM_JSON_STRING

create
   make_default, make_from_json

feature -- Access

   gender: CHARACTER_32
      -- symbol ♂ male OR ♀ female

   name: ZSTRING

   city: STRING_32

   age: INTEGER

end

regards
Finnian

Finnian Reilly

unread,
Sep 19, 2018, 9:59:29 AM9/19/18
to Eiffel Users
Class EL_JSON_NAME_VALUE_LIST

Another useful JSON class in Eiffel-Loop is EL_JSON_NAME_VALUE_LIST

For an example of it's use see routine new_json_fields in class HTTP_CONNECTION_TEST_SET

Larry Rix

unread,
Sep 19, 2018, 11:16:09 AM9/19/18
to Eiffel Users
BTW: I have added a couple of sample videos for the json_ext library found here on GitHub. Specifically, this video.

Larry Rix

unread,
Sep 19, 2018, 12:19:03 PM9/19/18
to Eiffel Users
Recursive example video is found here.

Jocelyn Fiat

unread,
Sep 19, 2018, 3:24:01 PM9/19/18
to Eiffel Users
Hi,

Thanks to those messages, I took the opportunity to integrate pending changes to eJSON library.

1) Especially adding the JSON_BASIC_SERIALIZATION  which is handling basic serialization and deserialization.
By "Basic" I mean, it uses predefined configuration to use STRING_TABLE, and ARRAYED_LIST to deserialize json text.

more or less
if attached (create {JSON_BASIC_SERIALIZATION}).table_from_json_string (json) as table then
    -- you get a STRING_TABLE [detachable ANY] ..and for json array, it uses ARRAYED_LIST [detachable ANY]
end

It may still not answer the need of Anders, and as I said early, one can also use directly the JSON_OBJECT as object representation.

2) JSON_VALUE has now an additional way to access data, called "chained_item".
If you have a json string:   { "person": { "name": "John Smith", "address": { "city": "New York" } } }
    a_json_value@"person"@"address"@"city"
    returns a JSON_STRING containing  "New York", and if this value does not exist, it returns JSON_NULL.

    note, to access the 3rd item of a json array,  use  a_json_value@"items"@"3" 
    (side note: If the alias "[]" was not already taken by JSON_ARRAY.i_th, we could have use a_json_value["items"]["3"]  ... but let's not break existing code.)

3) Added JSON_VALUE.same_string (READABLE_STRING_GENERAL)  and also same_caseless_string.
    It is relevant only for JSON_STRING values, but it allows to do in combination with chained_item
    if (a_json_value@"person"@"address"@"city").same_caseless_string ("new york") then
        ...

4) I also completed the "serialization" example with custom (de)serializer usage.
It uses a simple model, or PERSON, TEAM and PERSON_DETAILS (for address).

So this is still a bit more complex than Ander's need, but in case you need more advanced solution, you know a little bit more about the eJSON library.

Regards,
-- Jocelyn





On Wed, Sep 19, 2018 at 6:19 PM Larry Rix <lar...@moonshotsoftware.com> wrote:
Recursive example video is found here.

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
Visit this group at https://groups.google.com/group/eiffel-users.
For more options, visit https://groups.google.com/d/optout.

Larry Rix

unread,
Sep 19, 2018, 3:49:59 PM9/19/18
to Eiffel Users
Thanks for updating (I will try to pull those and integrate to my json_ext).

Otherwise, the existing json library is UGLY!

json_ugly.PNG


Larry Rix

unread,
Sep 19, 2018, 4:18:05 PM9/19/18
to Eiffel Users
And now we have (below). This is much better! Lines #17-23 are the old way and Lines #24-26 the new!

My json_ext library sets up a list of features that are to be serialized and deserialized. Does the eJSON library have facility to handle this? 

(re: my `make_from_json` feature for deserialization. The `json_out` handle serializing recursively.)

I now wonder if my json_ext library is needed going forward? If you watch those videos and see what I am doing, then perhaps you can give me feedback on what the way to do the same thing is in eJSON vs json_ext. If it is cleaner and better, then I can do video docs for the eJSON and abandon my json_ext, yes?


json_pretty.PNG


Jocelyn Fiat

unread,
Sep 19, 2018, 4:18:39 PM9/19/18
to Eiffel Users
Yes, the code you shared is the current way to do, it is safe, fully typed, but could be annoying to use as it makes the code more verbose.

Note you don't need to "attached {JSON_OBJECT} l_parser.parsed_json_object" ,but only "attached l_parser.parsed_json_object", as it is already typed with JSON_OBJECT.

So now, one will be able to do

if
   attached l_parser.parsed_json_object as j_object and then
   attached j_object@"person"@"address"@"city" as j_city
then
    print (j_city.representation)
else
    -- report json content error
end

if you want to access the j_city object, then no choice

if
   attached l_parser.parsed_json_object as j_object and then
   attached {JSON_STRING} j_object@"person"@"address"@"city" as j_city_string
then
    print ( j_city_string.unescaped_string_32) -- TODO: use localized_printer to proper handle unicode console output!
else
    -- report json content error
end

But be aware, the `chained_item alias "@" ` is useful, but you can not check if an attribute is really missing, as "null" is a possible value for an attribute, but I guess this limitation is not that critical

-- Jocelyn

(note: I would not encourage you to use the "check ... then end" too much. I only use it when there is no other clean way to prove void-safety, otherwise I usually use "if attached .... then else end"
in your code, the "else" would be a good place to report unexpected json content, rather than raising CHECK Violation exception. But this is another topic).

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
Visit this group at https://groups.google.com/group/eiffel-users.
For more options, visit https://groups.google.com/d/optout.

Larry Rix

unread,
Sep 20, 2018, 10:24:44 AM9/20/18
to Eiffel Users
Hi Jocelyn,

Thanks for your feedback! It is always valuable to me.

Yeah—the type check is not needed as you say. Sometimes, I do that just to be clear to myself as a reader, but that makes me wonder: By doing that, am I creating more work for the processor by the code that is produced? Is this an efficiency matter?
Reply all
Reply to author
Forward
0 new messages