From 9989aa3fee034a3c0e81fb67280fedc929cca6e3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 4 Aug 2011 14:33:21 -0400 Subject: [PATCH] lib: intro of json serialization module MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- lib/json/json.nit | 34 +++++++++++ lib/json/json_reader.nit | 122 +++++++++++++++++++++++++++++++++++++ lib/json/json_writer.nit | 143 ++++++++++++++++++++++++++++++++++++++++++++ lib/json/jsonable.nit | 61 +++++++++++++++++++ lib/json/jsonable.nit.args | 1 + tests/nitg-e.skip | 1 + tests/nitg-s.skip | 1 + tests/nitg.skip | 1 + tests/niti.skip | 1 + tests/sav/test_json.res | 40 +++++++++++++ tests/test_json.nit | 68 +++++++++++++++++++++ 11 files changed, 473 insertions(+) create mode 100644 lib/json/json.nit create mode 100644 lib/json/json_reader.nit create mode 100644 lib/json/json_writer.nit create mode 100644 lib/json/jsonable.nit create mode 100644 lib/json/jsonable.nit.args create mode 100644 tests/sav/test_json.res create mode 100644 tests/test_json.nit diff --git a/lib/json/json.nit b/lib/json/json.nit new file mode 100644 index 0000000..1e9522f --- /dev/null +++ b/lib/json/json.nit @@ -0,0 +1,34 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012-2013 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Writing to and reading from the Json format. +# Based on the json0 C library +module json + +import jsonable +import json_reader +import json_writer + +redef class String + fun json_load_from_file : nullable Map[ String, nullable Jsonable ] + do + var f = new IFStream.open( self ) + var data = f.read_all.json_to_object + f.close + + return data.as(not null) # ( Map[ String, nullable Jsonable ] ) + end +end diff --git a/lib/json/json_reader.nit b/lib/json/json_reader.nit new file mode 100644 index 0000000..3995f13 --- /dev/null +++ b/lib/json/json_reader.nit @@ -0,0 +1,122 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012-2013 Alexis Laferrière +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Deserialisation from the Json format to Nit objects +module json_reader + +intrude import jsonable + +redef class String + # Deserializes this String and return its value as a Map[String, nullable Jsonable] + # On error, null is returned. + fun json_to_object : nullable Map[String, nullable Jsonable] import String::from_cstring, JsonObject::json_to_map `{ + char *native_recv; + json_object *jobj; + nullable_Map map; + + native_recv = String_to_cstring( recv ); + jobj = json_tokener_parse( native_recv ); + map = JsonObject_json_to_map( jobj ); + + /*json_object_put( jobj );*/ + return map; + `} +end + +redef extern JsonObject + # Get this json object as a Map + private fun json_to_map : nullable Map[String, nullable Jsonable] import String::from_cstring, String::to_cstring, HashMap, HashMap::[]=, json_cross, HashMap[String,nullable Jsonable] as( nullable Map[String,nullable Jsonable] ), String as ( Object ), nullable Jsonable as (nullable Object) `{ + HashMap map; + String nit_key; + nullable_Jsonable nit_val; + + map = new_HashMap(); + + { /* prevents "mixed declaration and code" warning for C90 */ + json_object_object_foreach( recv, key, val ) { + nit_key = new_String_from_cstring( key ); + nit_val = JsonObject_json_cross( val , json_object_get_type( val ) ); + + HashMap__index_assign( map, String_as_Object( nit_key ), nullable_Jsonable_as_nullable_Object( nit_val ) ); + } + } + + return HashMap_as_nullable_Map( map ); + `} + + # Get this json object as a Bool + private fun json_to_bool : Bool `{ + return json_object_get_boolean( recv ); + `} + + # Get this json object as a Float + private fun json_to_float : Float `{ + return json_object_get_double( recv ); + `} + + # Get this json object as an Int + private fun json_to_int : Int `{ + return json_object_get_int( recv ); + `} + + # Get this json object as a Sequence + private fun json_to_sequence : Sequence[Jsonable] import Array, Array::push, Array[nullable Jsonable] as ( Sequence[nullable Jsonable] ), json_cross `{ + array_list* jlist; + json_object* jobj; + nullable_Jsonable obj; + Array dest; + int i; + int len; + + jlist = json_object_get_array( recv ); + len = json_object_array_length( recv ); + dest = new_Array(); + for ( i = 0; i < len; i ++ ) { + jobj = json_object_array_get_idx( recv, i ); + obj = JsonObject_json_cross( jobj, json_object_get_type( jobj ) ); + Array_push( dest, nullable_Jsonable_as_nullable_Object( obj ) ); + } + + return Array_as_Sequence( dest ); + `} + + # Get this json object as a String + private fun json_to_string : String import String::from_cstring `{ + char *cstring; + cstring = json_object_get_string( recv ); + return new_String_from_cstring( cstring ); + `} + + # Intermediate function to convert to gt this Json object as a given type. + # Imlemented in Nit because Nit should manage all possible typing-related work. + private fun json_cross( json_type : Int ) : nullable Jsonable + do + if json_type == 0 then # null + return null + else if json_type == 1 then # Bool + return json_to_bool + else if json_type == 2 then # Float + return json_to_float + else if json_type == 3 then # Int + return json_to_int + else if json_type == 4 then # Map + return json_to_map + else if json_type == 5 then # Sequence + return json_to_sequence + else if json_type == 6 then # String + return json_to_string + else + print "WARNING: Unrecongnized json object type id: {json_type}" + return null + end + end +end diff --git a/lib/json/json_writer.nit b/lib/json/json_writer.nit new file mode 100644 index 0000000..81946f3 --- /dev/null +++ b/lib/json/json_writer.nit @@ -0,0 +1,143 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012-2013 Alexis Laferrière +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Serialisation from Nit objects to the Json format +module json_writer + +intrude import jsonable + +redef interface Jsonable + # Get a JsonObject representing this instance, specific to the C library + private fun to_json_object : JsonObject is abstract +end + +# Will ignore non-jsonable +redef class Map[ K, V ] + # Get a json-formatted string of this map + fun to_json( pretty: Bool ) : String import to_json_object `{ + json_object *jobj; + char *json_native_string; + String json_string; + + jobj = Map_to_json_object( recv ); + if ( pretty ) + json_native_string = json_object_to_json_string_ext( jobj, JSON_C_TO_STRING_PRETTY ); + else + json_native_string = json_object_to_json_string_ext( jobj, JSON_C_TO_STRING_PLAIN ); + json_string = new_String_from_cstring( json_native_string ); + return json_string; + `} + + redef fun to_json_object + do + var jobj = new JsonObject + + var iter = iterator + while iter.is_ok do + var key = iter.key + if key isa String then + var val = iter.item + if val isa Jsonable then + var jsubobj = val.to_json_object + jobj.add( key, jsubobj ) + else if val == null then + jobj.add( key, null ) + else + print "WARNING: value \"{val}\" not jsonable, cannot be converted to json." + end + else + print "WARNING: key \"{key}\" not a string, cannot be converted to json." + end + + iter.next + end + return jobj + end +end + +redef class Sequence[ E ] + redef fun to_json_object + do + var jarray = new JsonArray + for e in self do + if e isa nullable Jsonable then + if e == null then + jarray.push( null ) + else + var obj = e.to_json_object + jarray.push( obj ) + end + else + print "WARNING: element \"{e}\" not a Jsonable, cannot be converted to json." + end + end + + return jarray + end +end + +redef class String + redef fun to_json_object import String::from_cstring `{ + char *native_recv = String_to_cstring( recv ); + return json_object_new_string( native_recv ); + `} +end + +redef class Int + redef fun to_json_object `{ + return json_object_new_int( recv ); + `} +end + +redef class Bool + redef fun to_json_object `{ + return json_object_new_boolean( recv ); + `} +end + +redef class Float + redef fun to_json_object `{ + return json_object_new_double( recv ); + `} +end + +redef class JsonObject + new `{ return json_object_new_object(); `} + + # Add a key and value to the object + fun add( key : String, val : nullable JsonObject ) import String::to_cstring, nullable JsonObject as not nullable `{ + char* native_key; + + native_key = String_to_cstring( key ); + + if ( JsonObject_is_null(val) ) { + json_object_object_add( recv, native_key, NULL ); + } else { + json_object *jobj; + jobj = JsonObject_as_not_null( val ); + json_object_object_add( recv, native_key, jobj ); + } + `} +end + +private extern JsonArray + super JsonObject + + new `{ return json_object_new_array(); `} + + fun push( val : nullable JsonObject ) `{ + if ( JsonObject_is_null(val) ) + json_object_array_add( recv, NULL ); + else + json_object_array_add( recv, JsonObject_as_not_null(val) ); + `} +end diff --git a/lib/json/jsonable.nit b/lib/json/jsonable.nit new file mode 100644 index 0000000..2a87ed9 --- /dev/null +++ b/lib/json/jsonable.nit @@ -0,0 +1,61 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012-2013 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Basic json related functionalities +module jsonable + +in "C Header" `{ + #define __STRICT_ANSI__ + #include +`} + +# Type supported by the Json format +interface Jsonable +end + +# Main object type used by C library +private extern JsonObject `{ json_object* `} + # Give up ownership of this object and decrease the reference count. + fun put `{ json_object_put( recv ); `} + + # Aquire ownership of this object and increase the reference count. + fun get `{ json_object_get( recv ); `} +end + +redef class Sequence[ V ] + super Jsonable +end + +# Can b converted to a Json object +redef class Map[ K, V ] + super Jsonable +end + +redef class String + super Jsonable +end + +redef class Int + super Jsonable +end + +redef class Bool + super Jsonable +end + +redef class Float + super Jsonable +end diff --git a/lib/json/jsonable.nit.args b/lib/json/jsonable.nit.args new file mode 100644 index 0000000..a21170f --- /dev/null +++ b/lib/json/jsonable.nit.args @@ -0,0 +1 @@ +--cc-lib-name json diff --git a/tests/nitg-e.skip b/tests/nitg-e.skip index 649d79e..8f01cfc 100644 --- a/tests/nitg-e.skip +++ b/tests/nitg-e.skip @@ -6,6 +6,7 @@ test_extern test_ni_ test_ffi_ test_gtk +test_json calculator nitc nitdoc diff --git a/tests/nitg-s.skip b/tests/nitg-s.skip index 649d79e..8f01cfc 100644 --- a/tests/nitg-s.skip +++ b/tests/nitg-s.skip @@ -6,6 +6,7 @@ test_extern test_ni_ test_ffi_ test_gtk +test_json calculator nitc nitdoc diff --git a/tests/nitg.skip b/tests/nitg.skip index 649d79e..8f01cfc 100644 --- a/tests/nitg.skip +++ b/tests/nitg.skip @@ -6,6 +6,7 @@ test_extern test_ni_ test_ffi_ test_gtk +test_json calculator nitc nitdoc diff --git a/tests/niti.skip b/tests/niti.skip index cdc3c9a..1aae86b 100644 --- a/tests/niti.skip +++ b/tests/niti.skip @@ -7,6 +7,7 @@ test_ffi_ test_math test_mem test_gtk +test_json calculator shoot_logic bench_ diff --git a/tests/sav/test_json.res b/tests/sav/test_json.res new file mode 100644 index 0000000..a20ecbf --- /dev/null +++ b/tests/sav/test_json.res @@ -0,0 +1,40 @@ +../lib/json/json_reader.nit: In function 'JsonObject_json_to_string___impl': +../lib/json/json_reader.nit:96:11: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] +../lib/json/json_writer.nit: In function 'Map_to_json___impl': +../lib/json/json_writer.nit:34:23: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] +../lib/json/json_writer.nit:36:23: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] +{"int":"1234","float":"0.1234","str":"str","null": null} +{"int":1234,"float":0.123400,"str":"str","null":null} +{ + "int":1234, + "float":0.123400, + "str":"str", + "null":null +} +{"arr":"123","obj":"{"int":"123","float":"-234.449997"}"} +{"arr":[1,2,3],"obj":{"int":123,"float":-234.449997}} +{ + "arr":[ + 1, + 2, + 3 + ], + "obj":{ + "int":123, + "float":-234.449997 + } +} +{"arr":"12.3str","obj":"{"int":"123","float":"-234.449997"}"} +{"arr":[1,2.300000,null,"str"],"obj":{"int":123,"float":-234.449997}} +{ + "arr":[ + 1, + 2.300000, + null, + "str" + ], + "obj":{ + "int":123, + "float":-234.449997 + } +} diff --git a/tests/test_json.nit b/tests/test_json.nit new file mode 100644 index 0000000..1a06290 --- /dev/null +++ b/tests/test_json.nit @@ -0,0 +1,68 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012-2013 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module test_json + +import json + +redef class HashMap[K,V] + redef fun to_s + do + var es = new Array[String] + for k, v in self do + if v != null then + es.add( "\"{k}\":\"{v}\"" ) + else + es.add( "\"{k}\": null" ) + end + end + return "\{{es.join(",")}\}" + end +end + +redef class String + fun parse_and_display + do + var json_map = json_to_object + if json_map != null then + print json_map + print json_map.to_json(false) + print json_map.to_json(true) + else + print "Conversion to json failed." + end + end +end + +fun print_usage do print "Usage: json input.json" + +if args.length == 1 then + var input_path = args.first + var input_file = new IFStream.open( input_path ) + var input_text = input_file.read_all + input_file.close + + input_text.parse_and_display +else + var s = "\{\"int\":1234, \"float\":0.1234, \"str\":\"str\", \"null\":null\}" + s.parse_and_display + + s = "\{\"arr\":[1,2,3], \"obj\":\{\"int\":123, \"float\":-234.45\}\}" + s.parse_and_display + + s = "\{\"arr\":[1,2.3,null,\"str\"], \"obj\":\{\"int\":123, \"float\":-234.45\}\}" + s.parse_and_display +end -- 1.7.9.5