This commit is contained in:
Jocelyn Fiat
2012-11-15 16:29:02 +01:00
5 changed files with 240 additions and 231 deletions

View File

@@ -0,0 +1 @@
*.swp

View File

@@ -64,7 +64,11 @@ feature -- Conversion
until until
c.after c.after
loop loop
create js.make_json (c.key.out) if attached {JSON_STRING} json.value (c.key) as l_key then
js := l_key
else
create js.make_json (c.key.out)
end
jv := json.value (c.item) jv := json.value (c.item)
if jv /= Void then if jv /= Void then
Result.put (jv, js) Result.put (jv, js)

View File

@@ -71,7 +71,11 @@ feature -- Conversion
until until
c.after c.after
loop loop
create js.make_json (c.key.out) if attached {JSON_STRING} json.value (c.key) as l_key then
js := l_key
else
create js.make_json (c.key.out)
end
jv := json.value (c.item) jv := json.value (c.item)
if jv /= Void then if jv /= Void then
Result.put (jv, js) Result.put (jv, js)

View File

@@ -1,268 +1,268 @@
note note
description: "Core factory class for creating JSON objects and corresponding Eiffel objects." description: "Core factory class for creating JSON objects and corresponding Eiffel objects."
author: "Paul Cohen" author: "Paul Cohen"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
file: "$HeadURL: $" file: "$HeadURL: $"
class EJSON class EJSON
inherit inherit
EXCEPTIONS EXCEPTIONS
feature -- Access feature -- Access
value (an_object: detachable ANY): detachable JSON_VALUE value (an_object: detachable ANY): detachable JSON_VALUE
-- JSON value from Eiffel object. Raises an "eJSON exception" if -- JSON value from Eiffel object. Raises an "eJSON exception" if
-- unable to convert value. -- unable to convert value.
local local
i: INTEGER i: INTEGER
ja: JSON_ARRAY ja: JSON_ARRAY
do do
-- Try to convert from basic Eiffel types. Note that we check with -- Try to convert from basic Eiffel types. Note that we check with
-- `conforms_to' since the client may have subclassed the base class -- `conforms_to' since the client may have subclassed the base class
-- that these basic types are derived from. -- that these basic types are derived from.
if an_object = Void then if an_object = Void then
create {JSON_NULL} Result create {JSON_NULL} Result
elseif attached {BOOLEAN} an_object as b then elseif attached {BOOLEAN} an_object as b then
create {JSON_BOOLEAN} Result.make_boolean (b) create {JSON_BOOLEAN} Result.make_boolean (b)
elseif attached {INTEGER_8} an_object as i8 then elseif attached {INTEGER_8} an_object as i8 then
create {JSON_NUMBER} Result.make_integer (i8) create {JSON_NUMBER} Result.make_integer (i8)
elseif attached {INTEGER_16} an_object as i16 then elseif attached {INTEGER_16} an_object as i16 then
create {JSON_NUMBER} Result.make_integer (i16) create {JSON_NUMBER} Result.make_integer (i16)
elseif attached {INTEGER_32} an_object as i32 then elseif attached {INTEGER_32} an_object as i32 then
create {JSON_NUMBER} Result.make_integer (i32) create {JSON_NUMBER} Result.make_integer (i32)
elseif attached {INTEGER_64} an_object as i64 then elseif attached {INTEGER_64} an_object as i64 then
create {JSON_NUMBER} Result.make_integer (i64) create {JSON_NUMBER} Result.make_integer (i64)
elseif attached {NATURAL_8} an_object as n8 then elseif attached {NATURAL_8} an_object as n8 then
create {JSON_NUMBER} Result.make_natural (n8) create {JSON_NUMBER} Result.make_natural (n8)
elseif attached {NATURAL_16} an_object as n16 then elseif attached {NATURAL_16} an_object as n16 then
create {JSON_NUMBER} Result.make_natural (n16) create {JSON_NUMBER} Result.make_natural (n16)
elseif attached {NATURAL_32} an_object as n32 then elseif attached {NATURAL_32} an_object as n32 then
create {JSON_NUMBER} Result.make_natural (n32) create {JSON_NUMBER} Result.make_natural (n32)
elseif attached {NATURAL_64} an_object as n64 then elseif attached {NATURAL_64} an_object as n64 then
create {JSON_NUMBER} Result.make_natural (n64) create {JSON_NUMBER} Result.make_natural (n64)
elseif attached {REAL_32} an_object as r32 then elseif attached {REAL_32} an_object as r32 then
create {JSON_NUMBER} Result.make_real (r32) create {JSON_NUMBER} Result.make_real (r32)
elseif attached {REAL_64} an_object as r64 then elseif attached {REAL_64} an_object as r64 then
create {JSON_NUMBER} Result.make_real (r64) create {JSON_NUMBER} Result.make_real (r64)
elseif attached {ARRAY [detachable ANY]} an_object as a then elseif attached {ARRAY [detachable ANY]} an_object as a then
create ja.make_array create ja.make_array
from from
i := a.lower i := a.lower
until until
i > a.upper i > a.upper
loop loop
if attached value (a @ i) as v then if attached value (a @ i) as v then
ja.add (v) ja.add (v)
else else
check value_attached: False end check value_attached: False end
end end
i := i + 1 i := i + 1
end end
Result := ja Result := ja
elseif attached {CHARACTER_8} an_object as c8 then elseif attached {CHARACTER_8} an_object as c8 then
create {JSON_STRING} Result.make_json (c8.out) create {JSON_STRING} Result.make_json (c8.out)
elseif attached {CHARACTER_32} an_object as c32 then elseif attached {CHARACTER_32} an_object as c32 then
create {JSON_STRING} Result.make_json (c32.out) create {JSON_STRING} Result.make_json (c32.out)
elseif attached {STRING_8} an_object as s8 then elseif attached {STRING_8} an_object as s8 then
create {JSON_STRING} Result.make_json (s8) create {JSON_STRING} Result.make_json (s8)
elseif attached {STRING_32} an_object as s32 then elseif attached {STRING_32} an_object as s32 then
create {JSON_STRING} Result.make_json_from_string_32 (s32) create {JSON_STRING} Result.make_json_from_string_32 (s32)
end end
if Result = Void then if Result = Void then
-- Now check the converters -- Now check the converters
if an_object /= Void and then attached converter_for (an_object) as jc then if an_object /= Void and then attached converter_for (an_object) as jc then
Result := jc.to_json (an_object) Result := jc.to_json (an_object)
else else
raise (exception_failed_to_convert_to_json (an_object)) raise (exception_failed_to_convert_to_json (an_object))
end end
end end
end end
object (a_value: detachable JSON_VALUE; base_class: detachable STRING): detachable ANY object (a_value: detachable JSON_VALUE; base_class: detachable STRING): detachable ANY
-- Eiffel object from JSON value. If `base_class' /= Void an eiffel -- Eiffel object from JSON value. If `base_class' /= Void an eiffel
-- object based on `base_class' will be returned. Raises an "eJSON -- object based on `base_class' will be returned. Raises an "eJSON
-- exception" if unable to convert value. -- exception" if unable to convert value.
local local
i: INTEGER i: INTEGER
ll: LINKED_LIST [detachable ANY] ll: LINKED_LIST [detachable ANY]
t: HASH_TABLE [detachable ANY, STRING_GENERAL] t: HASH_TABLE [detachable ANY, STRING_GENERAL]
keys: ARRAY [JSON_STRING] keys: ARRAY [JSON_STRING]
do do
if a_value = Void then if a_value = Void then
Result := Void Result := Void
else else
if base_class = Void then if base_class = Void then
if a_value = Void then if a_value = Void then
Result := Void Result := Void
elseif attached {JSON_NULL} a_value then elseif attached {JSON_NULL} a_value then
Result := Void Result := Void
elseif attached {JSON_BOOLEAN} a_value as jb then elseif attached {JSON_BOOLEAN} a_value as jb then
Result := jb.item Result := jb.item
elseif attached {JSON_NUMBER} a_value as jn then elseif attached {JSON_NUMBER} a_value as jn then
if jn.item.is_integer_8 then if jn.item.is_integer_8 then
Result := jn.item.to_integer_8 Result := jn.item.to_integer_8
elseif jn.item.is_integer_16 then elseif jn.item.is_integer_16 then
Result := jn.item.to_integer_16 Result := jn.item.to_integer_16
elseif jn.item.is_integer_32 then elseif jn.item.is_integer_32 then
Result := jn.item.to_integer_32 Result := jn.item.to_integer_32
elseif jn.item.is_integer_64 then elseif jn.item.is_integer_64 then
Result := jn.item.to_integer_64 Result := jn.item.to_integer_64
elseif jn.item.is_natural_64 then elseif jn.item.is_natural_64 then
Result := jn.item.to_natural_64 Result := jn.item.to_natural_64
elseif jn.item.is_double then elseif jn.item.is_double then
Result := jn.item.to_double Result := jn.item.to_double
end end
elseif attached {JSON_STRING} a_value as js then elseif attached {JSON_STRING} a_value as js then
create {STRING_32} Result.make_from_string (js.unescaped_string_32) create {STRING_32} Result.make_from_string (js.unescaped_string_32)
elseif attached {JSON_ARRAY} a_value as ja then elseif attached {JSON_ARRAY} a_value as ja then
from from
create ll.make create ll.make
i := 1 i := 1
until until
i > ja.count i > ja.count
loop loop
ll.extend (object (ja [i], Void)) ll.extend (object (ja [i], Void))
i := i + 1 i := i + 1
end end
Result := ll Result := ll
elseif attached {JSON_OBJECT} a_value as jo then elseif attached {JSON_OBJECT} a_value as jo then
keys := jo.current_keys keys := jo.current_keys
create t.make (keys.count) create t.make (keys.count)
from from
i := keys.lower i := keys.lower
until until
i > keys.upper i > keys.upper
loop loop
if attached {STRING_GENERAL} object (keys [i], Void) as s then if attached {STRING_GENERAL} object (keys [i], Void) as s then
t.put (object (jo.item (keys [i]), Void), s) t.put (object (jo.item (keys [i]), Void), s)
end end
i := i + 1 i := i + 1
end end
Result := t Result := t
end end
else else
if converters.has_key (base_class) and then attached converters.found_item as jc then if converters.has_key (base_class) and then attached converters.found_item as jc then
Result := jc.from_json (a_value) Result := jc.from_json (a_value)
else else
raise (exception_failed_to_convert_to_eiffel (a_value, base_class)) raise (exception_failed_to_convert_to_eiffel (a_value, base_class))
end end
end end
end end
end end
object_from_json (json: STRING; base_class: detachable STRING): detachable ANY object_from_json (json: STRING; base_class: detachable STRING): detachable ANY
-- Eiffel object from JSON representation. If `base_class' /= Void an -- Eiffel object from JSON representation. If `base_class' /= Void an
-- Eiffel object based on `base_class' will be returned. Raises an -- Eiffel object based on `base_class' will be returned. Raises an
-- "eJSON exception" if unable to convert value. -- "eJSON exception" if unable to convert value.
require require
json_not_void: json /= Void json_not_void: json /= Void
local local
jv: detachable JSON_VALUE jv: detachable JSON_VALUE
do do
json_parser.set_representation (json) json_parser.set_representation (json)
jv := json_parser.parse jv := json_parser.parse
if jv /= Void then if jv /= Void then
Result := object (jv, base_class) Result := object (jv, base_class)
end end
end end
converter_for (an_object: ANY): detachable JSON_CONVERTER converter_for (an_object: ANY): detachable JSON_CONVERTER
-- Converter for objects. Returns Void if none found. -- Converter for objects. Returns Void if none found.
require require
an_object_not_void: an_object /= Void an_object_not_void: an_object /= Void
do do
if converters.has_key (an_object.generator) then if converters.has_key (an_object.generator) then
Result := converters.found_item Result := converters.found_item
end end
end end
json_reference (s: STRING): JSON_OBJECT json_reference (s: STRING): JSON_OBJECT
-- A JSON (Dojo style) reference object using `s' as the -- A JSON (Dojo style) reference object using `s' as the
-- reference value. The caller is responsable for ensuring -- reference value. The caller is responsable for ensuring
-- the validity of `s' as a json reference. -- the validity of `s' as a json reference.
require require
s_not_void: s /= Void s_not_void: s /= Void
local local
js_key, js_value: JSON_STRING js_key, js_value: JSON_STRING
do do
create Result.make create Result.make
create js_key.make_json ("$ref") create js_key.make_json ("$ref")
create js_value.make_json (s) create js_value.make_json (s)
Result.put (js_value, js_key) Result.put (js_value, js_key)
end end
json_references (l: LIST [STRING]): JSON_ARRAY json_references (l: LIST [STRING]): JSON_ARRAY
-- A JSON array of JSON (Dojo style) reference objects using the -- A JSON array of JSON (Dojo style) reference objects using the
-- strings in `l' as reference values. The caller is responsable -- strings in `l' as reference values. The caller is responsable
-- for ensuring the validity of all strings in `l' as json -- for ensuring the validity of all strings in `l' as json
-- references. -- references.
require require
l_not_void: l /= Void l_not_void: l /= Void
local local
c: ITERATION_CURSOR [STRING] c: ITERATION_CURSOR [STRING]
do do
create Result.make_array create Result.make_array
from from
c := l.new_cursor c := l.new_cursor
until until
c.after c.after
loop loop
Result.add (json_reference (c.item)) Result.add (json_reference (c.item))
c.forth c.forth
end end
end end
feature -- Change feature -- Change
add_converter (jc: JSON_CONVERTER) add_converter (jc: JSON_CONVERTER)
-- Add the converter `jc'. -- Add the converter `jc'.
require require
jc_not_void: jc /= Void jc_not_void: jc /= Void
do do
converters.force (jc, jc.object.generator) converters.force (jc, jc.object.generator)
ensure ensure
has_converter: converter_for (jc.object) /= Void has_converter: converter_for (jc.object) /= Void
end end
feature {NONE} -- Implementation feature {NONE} -- Implementation
converters: HASH_TABLE [JSON_CONVERTER, STRING] converters: HASH_TABLE [JSON_CONVERTER, STRING]
-- Converters hashed by generator (base class) -- Converters hashed by generator (base class)
once once
create Result.make (10) create Result.make (10)
end end
feature {NONE} -- Implementation (Exceptions) feature {NONE} -- Implementation (Exceptions)
exception_prefix: STRING = "eJSON exception: " exception_prefix: STRING = "eJSON exception: "
exception_failed_to_convert_to_eiffel (a_value: JSON_VALUE; base_class: detachable STRING): STRING exception_failed_to_convert_to_eiffel (a_value: JSON_VALUE; base_class: detachable STRING): STRING
-- Exception message for failing to convert a JSON_VALUE to an instance of `a'. -- Exception message for failing to convert a JSON_VALUE to an instance of `a'.
do do
Result := exception_prefix + "Failed to convert JSON_VALUE to an Eiffel object: " + a_value.generator Result := exception_prefix + "Failed to convert JSON_VALUE to an Eiffel object: " + a_value.generator
if base_class /= Void then if base_class /= Void then
Result.append (" -> " + base_class) Result.append (" -> {" + base_class + "}")
end end
end end
exception_failed_to_convert_to_json (an_object: detachable ANY): STRING exception_failed_to_convert_to_json (an_object: detachable ANY): STRING
-- Exception message for failing to convert `a' to a JSON_VALUE. -- Exception message for failing to convert `a' to a JSON_VALUE.
do do
Result := exception_prefix + "Failed to convert Eiffel object to a JSON_VALUE" Result := exception_prefix + "Failed to convert Eiffel object to a JSON_VALUE"
if an_object /= Void then if an_object /= Void then
Result := ": " + an_object.generator Result.append (" : {" + an_object.generator + "}")
end end
end end
feature {NONE} -- Implementation (JSON parser) feature {NONE} -- Implementation (JSON parser)
json_parser: JSON_PARSER json_parser: JSON_PARSER
once once
create Result.make_parser ("") create Result.make_parser ("")
end end
end -- class EJSON end -- class EJSON

View File

@@ -632,7 +632,7 @@ feature -- Test
jrep := "%"foo\\bar%"" jrep := "%"foo\\bar%""
create parser.make_parser (jrep) create parser.make_parser (jrep)
if attached {JSON_STRING} parser.parse as jstring then if attached {JSON_STRING} parser.parse as jstring then
assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string.same_string ("foo\bar")) assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string_8.same_string ("foo\bar"))
end end
create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/") create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/")