diff --git a/History.txt b/History.txt index 02fd7cb6..96ec4ab0 100644 --- a/History.txt +++ b/History.txt @@ -5,6 +5,8 @@ team: "" date: "2011-07-06" revision: "0.3.0" +WARNING: THIS FILE IS NOT UP TO DATE + +++++++++++++++++++++Important Changes since 0.2.0 version++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/License.txt b/License.txt index 56c30878..36d303a0 100644 --- a/License.txt +++ b/License.txt @@ -1,4 +1,4 @@ -Copyright (c) 2010-2014 Javier Velilla and others, +Copyright (c) 2010-2014 Javier Velilla, Jocelyn Fiat and others, https://github.com/eiffelhub/json . diff --git a/Readme.txt b/Readme.txt index c0085d5d..44f30cb7 100644 --- a/Readme.txt +++ b/Readme.txt @@ -1,21 +1,26 @@ Readme file for eJSON ===================== -team: "Javier Velilla,Jocelyn Fiat, Paul Cohen" -date: "$Date$" -revision: "$Revision$" +team: "Javier Velilla, Jocelyn Fiat" +previous contributors: "Paul Cohen" +date: "2014-nov-17" 1. Introduction --------------- eJSON stands for Eiffel JSON library and is a small Eiffel library for dealing -with the JSON format. The objective of the library is to provide two basic -features Eiffel2JSON and JSON2Eiffel. +with the JSON format. This library provides a JSON parser and visitors, +including a pretty printer. + +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. 2. Legal stuff -------------- -eJSON is copyrighted by the author Javier Velilla and others. It is licensed +eJSON is copyrighted by the author Javier Velilla, Jocelyn Fiat and others. It is licensed under the MIT License. See the file license.txt in the same directory as this readme file. @@ -46,18 +51,18 @@ Currently the only documentation on eJSON is available at: EJSON requires that you have: -1. Gobo 3.9 installed or later -2. One of the following compiler combinations installed: - * ISE Eiffel 6.5 or later. +1. One of the following compiler combinations installed: + * ISE Eiffel 13.11 or later. * gec [try to test] - * tecomp [try to test] eJSON probably works fine with other versions of the above compilers. There are no known platform dependencies (Windows, Linux). To install eJSON simply extract the ejson-X.Y.Z.zip file to some appropriate place on your hard disk. There are no requirements on environment variables or -registry variables. +registry variables. +Note eJSON is also delivered within EiffelStudio release, under +$ISE_LIBRARY/contrib/library/text/parser/json To verify that everything works you should compile the example programs and/or the test program. @@ -70,18 +75,18 @@ installation. Directory Description --------- ----------- -doc Contains the eJSON.pdf documentation file. -examples Contains the two example programs. -ejson Contains the actual eJSON library classes. -test Contains a test program for eJSON. +doc Contains documentation file. +examples Contains example codes. +library Contains the actual eJSON library classes. +test Contains test suite for eJSON. 7. Contacting the Team ---------------------- Contact the team: + https://github.com/eiffelhub/json/issues Javier Velilla «javier.hector@gmail.com» - Paul Cohen «paco@seibostudios.se» Jocelyn Fiat «jfiat@eiffel.com» 8. Releases @@ -92,8 +97,16 @@ history.txt. Version Date Description ------- ---- ----------- +0.6.0 2014-11-17 Fixed various issue with parsing string (such as \t and related), + Implemented escaping of slash '/' only in case of ' + Many feature renaming to match Eiffel style and naming convention, + kept previous feature as obsolete. + Restructured the library to make easy extraction of "converter" + classes if needed in the future. + Marked converters classes as obsolete. 0.5.0 2013-11-dd Added JSON_ITERATOR, simplified JSON_OBJECT 0.4.0 2012-12-12 Updated documentation URI 0.3.0 2011-07-06 JSON Factory Converters 0.2.0 2010-02-07 Adapted to EiffelStudio 6.4 or later, supports void-safety -0.1.0 2010-02-07 First release, Adapted to SmartEiffel 1.2r7 and EiffelStudio 6.2 or previous \ No newline at end of file +0.1.0 2010-02-07 First release, Adapted to SmartEiffel 1.2r7 and EiffelStudio 6.2 or previous diff --git a/doc/user_guide.mediawiki b/doc/user_guide.mediawiki new file mode 100644 index 00000000..c4ab9b3e --- /dev/null +++ b/doc/user_guide.mediawiki @@ -0,0 +1,295 @@ +== Preface == + +This document is a living document! As always read and try out the code to understand what's really going on. + +=== About the project === + +The eJSON project was started by Javier Velilla in 2008. The aim was simply to +provide JSON support to Eiffel programmers. A couple of other people have been +involved to various extent since the start; Berend de Boer, Jocelyn Fiat and +Manu Stapf. In 2009 Paul Cohen joined the project as an active developer and +later Jocelyn Fiat. + +The current active maintainers: +- Javier Velilla +- Jocelyn Fiat + +The formal name of the project is “eJSON”. + +For questions regarding eJSON please contact +- +- +- or directly on [https://github.com/eiffelhub/json/issues] + +=== Current version and status === + +The latest release is 0.6.0. eJSON has been improved and cleaned. +The converters are not obsolete. + + +== Introduction == + +=== What is JSON? === + +JSON (JavaScript Object Notation) is a lightweight computer data interchange format. It is a text-based, human-readable format for representing simple data structures and associative arrays (called objects). See the [http://en.wikipedia.org/wiki/JSON Wikipedia article on JSON], [http://www.json.org www.json.org] and [http://www.json.com www.json.com] for more information. + +The JSON format is specified in [http://www.ietf.org/rfc/rfc4627.txt IETF RFC 4627] by Douglas Crockford. The official [http://www.iana.org/assignments/media-types Internet MIME media type] for JSON is "application/json". The recommended file name extension for JSON files is ".json". + +=== Advantages === + +1. Lightweight data-interchange format. +2. Easy for humans to read and write. +3. Enables easy integration with AJAX/JavaScript web applications. See the article [http://www.developer.com/lang/jscript/article.php/3596836 Speeding Up AJAX with JSON] for a good short discussion on this subject. +4. JSON data structures translate with ease into the native data structures universal to almost all programming languages used today. + +=== Use in Eiffel applications === + +JSON can be used as a general serialization format for Eiffel objects. As such it could be used as a: + +* Data representation format in REST-based web service applications written in Eiffel. +* Serialization format for Eiffel objects in persistence solutions. +* File format for configuration files in Eiffel systems. + +=== Prerequisites === + +eJSON works today with EiffelStudio 13.11 +There is an optional extension that requires the latest snapshot of the Gobo Eiffel libraries (a working snapshot is distributed with EiffelStudio). The depencencies on Gobo are on Gobo's unicode +and regex libraries and for some of the extra features in eJSON, on Gobos structure classes DS_HASH_TABLE and DS_LINKED_LIST. + +eJSON is intended to work with all ECMA compliant Eiffel compilers. + +=== Installation === + +You can either download a given release and install on your machine or you can get the latest snapshot of the code. +To download go to the [http://ejson.origo.ethz.ch/download download page]. +To get the latest snapshot of the code do: + +: $ git clone https://github.com/eiffelhub/json.git json + +*[https://github.com/eiffelhub/json/releases download page] +*[https://github.com/eiffelhub/json github project] + +Note that the latest json release is also delivered with EiffelStudio installation under $ISE_LIBRARY/contrib/library/text/parser/json. + + +=== Cluster and directory layout === + + json/ + library/ (Root directory for eJSON library classes) + kernel/ (All classes in this cluster should eventually only depend on ECMA Eiffel and FreeELKS). + json_array.e + json_boolean.e + json_null.e + json_number.e + json_object.e + json_string.e + json_value.e + parser/ + json_parser.e + json_parser_access.e + json_reader.e + json_tokens.e + utility/ + file/ + json_file_reader.e + visitor/ + json_visitor.e + json_iterator.e + json_pretty_string_visitor.e + print_json_visitor.e + converters/ (JSON core converter classes !OBSOLETE!) + json_converter.e + json_hash_table_converter.e + json_list_converter.e + json_linked_list_converter.e + json_arrayed_list_converter.e + support/ + ejson.e + shared_ejson.e + gobo_converters/ (JSON core converter classes support for GOBO !OBSOLETE!) + converters/ + json_ds_hash_table_converter.e + json_ds_linked_list_converter.e + shared_gobo_ejson.e + test/ (Contains autotest suite) + autotest/ (AutoTest based unit test). + examples/ (Example code) + +=== Future development === + +Here is a list of suggestions for future development of eJSON. +* Ongoing: Provide a JSON_FACTORY class for easy conversion between arbitrary JSON and Eiffel values. +* Ongoing: Provide a mechanism for users to add custom converters between JSON values and user space Eiffel classes. +* Ongoing: Implement a full test framework for eJSON. +* Suggestion: Investigate performance and improve it if neccessary. +* Suggestion: Support JSON references. See [http://www.json.com/2007/10/19/json-referencing-proposal-and-library JSON Referencing Proposal and Library] and [http://www.sitepen.com/blog/2008/06/17/json-referencing-in-dojo JSON referencing in Dojo] for more information. +* Suggestion: Support JSON path. See [http://goessner.net/articles/JsonPath JSONPath - XPath for JSON] for more information. +* Suggestion: Support JSON schema validation. See [http://json-schema.org JSON Schema Proposal] for more information. +* Suggestion: Support RDF JSON serialization. See [http://n2.talis.com/wiki/RDF_JSON_Specification RDF JSON Specification] for more information. +* Suggestion: Add support to JSON classes for conversion from Eiffel manifest values. So one can write things like: + +== A simple example == + +There are two basic approaches to using eJSON; either you use the basic JSON_VALUE classes, converting to and from JSON values to corresponding Eiffel instances or you use the high level eJSON interface class SHARED_EJSON. Of course you can use a mix of both approaches if you find it appropriate! + +Here is an example of how to create a JSON number value from an INTEGER and then obtain the JSON representation for that value. + + simple_example is + local + i: INTEGER + jn: JSON_NUMBER + s: STRING + do + i := 42 + create jn.make_integer (i) + s := jn.representation -- s.is_equal ("42") + end + +== Mapping of JSON values to Eiffel values == + +=== JSON number === + +JSON numbers are represented by the class JSON_NUMBER. JSON number values can be converted to/from NATURAL_*, INTEGER_* and REAL_64 values. For floating point values REAL_* is used. The complete mapping is as follows: + +JSON number -> Eiffel: +* -128 <= n <= +127 -> INTEGER_8 +* n can't be represented by INTEGER_8 and -32768 <= n <= +32767 -> INTEGER_16 +* n can't be represented by INTEGER_16 and -2147483648 <= n <= +2147483647 -> INTEGER_32 +* n can't be represented by INTEGER_32 and -9223372036854775808 <= n <= +9223372036854775807 -> INTEGER_64 +* n can't be represented by INTEGER_64 and 9223372036854775808 <= n <= 18446744073709551615 -> NATURAL_64 +* n has fractional dot '.' -> REAL_64. +* n -> eJSON exception if number can't be represented by a INTEGER_64, NATURAL_64 or REAL_64. + +Eiffel -> JSON number: +* NATURAL_8, NATURAL_16, NATURAL_32, NATURAL_64, NATURAL -> JSON number +* INTEGER_8, INTEGER_16, INTEGER_32, INTEGER_64, INTEGER -> JSON number +* REAL_32, REAL_64, REAL -> JSON number + +You can use the following creation routines to create JSON_NUMBER instances: + +* JSON_NUMBER.make_integer +* JSON_NUMBER.make_real +* JSON_NUMBER.make_natural + + eiffel_to_json_number_representation is + local + i: INTEGER + r: REAL + jn: JSON_NUMBER + do + print ("JSON representation of Eiffel INTEGER: '") + i := 123 + create jn.make_integer (i) + print (jn.representation) + print ("'%N") + print ("JSON representation of Eiffel REAL: '") + r := 12.3 + create jn.make_real (r) + print (jn.representation) + print ("'%N") + end + +The output of the above code will be: + + JSON representation of Eiffel INTEGER: '123' + JSON representation of Eiffel REAL: '12.300000190734863' + +=== JSON boolean === + +JSON boolean values are represented by the class JSON_BOOLEAN. The JSON boolean value "true" is converted to/from the BOOLEAN value "True" and the JSON boolean value "false is converted to/from the BOOLEAN value "False". + + eiffel_to_json_boolean_representation is + local + b: BOOLEAN + jb: JSON_BOOLEAN + do + print ("JSON representation of Eiffel BOOLEAN: '") + b := True + create jb.make (b) + print (jb.representation) + print ("'%N") + print("JSON representation of Eiffel BOOLEAN: '") + b := False + create jb.make (b) + print (jb.representation) + print ("'%N") + end + +The output of the above code will be: + + JSON representation of Eiffel BOOLEAN: 'true' + JSON representation of Eiffel BOOLEAN: 'false' + +=== JSON string === + +JSON strings are represented by the class JSON_STRING. JSON string values can be converted to/from STRING_32, STRING and CHARACTER values. The complete mapping is as follows: + +JSON string -> Eiffel: +* All JSON strings -> STRING or STRING_32 + +Eiffel -> JSON string: +* STRING_32 -> JSON string +* STRING -> JSON string +* CHARACTER -> JSON string + + eiffel_to_json_string_representation is + local + s: STRING + js: JSON_STRING + do + print ("JSON representation of Eiffel STRING: '") + s := "JSON rocks!" + create js.make_from_string (s) + print (js.representation) + print ("'%N") + end + +The output of the above code will be: + + JSON representation of Eiffel STRING: '"JSON rocks!"' + +Note: JSON escape unicode characters, as well a other specific characters, to get the unescaped string value, use either 'unescaped_string_8' or 'unescaped_string_32'. + +=== JSON null === + +The JSON null value is represented by the class JSON_NULL. The JSON null value can be converted to/from Void. + + eiffel_to_json_null_representation is + local + a: ANY + jn: JSON_NULL + do + create jn + print ("JSON representation for JSON null value: '") + print (jn.representation) + print ("'%N") + a := Void + if attached {JSON_NULL} json.value (a) as l_jn then -- json from SHARED_EJSON! + print ("JSON representation of Eiffel Void reference: '") + print (l_jn.representation) + print ("'%N") + end + end + +The output of the above code will be: + + JSON representation for JSON null value: 'null' + JSON representation of Eiffel Void reference: 'null' + +=== JSON array === + +JSON array is represented by the class JSON_ARRAY. + +=== JSON object === + +JSON object is represented by the class JSON_OBJECT. + + +== The eJSON visitor pattern == + +TBD. See examples. + +== The eJSON file reader class == + +TBD. + diff --git a/examples/basic/basic.e b/examples/basic/basic.e new file mode 100644 index 00000000..52520394 --- /dev/null +++ b/examples/basic/basic.e @@ -0,0 +1,83 @@ +class + BASIC + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + local + parser: JSON_PARSER + printer: JSON_PRETTY_STRING_VISITOR + s: STRING_32 + do + -- Create parser for content `json_content' + create parser.make_with_string (json_content) + -- Parse the content + parser.parse_content + if + parser.is_valid and then + attached parser.parsed_json_value as jv + then + -- Json content is valid, and well parser. + -- and the parsed json value is `jv' + + -- Let's access the glossary/title value + if + attached {JSON_OBJECT} jv as j_object and then + attached {JSON_OBJECT} j_object.item ("glossary") as j_glossary and then + attached {JSON_STRING} j_glossary.item ("title") as j_title + then + print ("The glossary title is %"" + j_title.unescaped_string_8 + "%".%N") + else + print ("The glossary title was not found!%N") + end + + -- Pretty print the parsed JSON + create s.make_empty + create printer.make (s) + jv.accept (printer) + print ("The JSON formatted using a pretty printer:%N") + print (s) + end + end + +feature -- Status + +feature -- Access + + json_content: STRING = "[ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} + ]" + +feature -- Change + +feature {NONE} -- Implementation + +invariant +-- invariant_clause: True + +end diff --git a/examples/basic/basic.ecf b/examples/basic/basic.ecf new file mode 100644 index 00000000..17ceee6f --- /dev/null +++ b/examples/basic/basic.ecf @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/library/json-safe.ecf b/library/json-safe.ecf index 8025f959..0a4e9d75 100644 --- a/library/json-safe.ecf +++ b/library/json-safe.ecf @@ -8,7 +8,7 @@ /CVS$ /.svn$ - diff --git a/library/json.rc b/library/json.rc deleted file mode 100644 index 8b137891..00000000 --- a/library/json.rc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/library/utility/visitor/json_pretty_string_visitor.e b/library/utility/visitor/json_pretty_string_visitor.e index 65e69551..e89c215c 100644 --- a/library/utility/visitor/json_pretty_string_visitor.e +++ b/library/utility/visitor/json_pretty_string_visitor.e @@ -10,7 +10,8 @@ inherit JSON_VISITOR create - make, make_custom + make, + make_custom feature -- Initialization @@ -32,14 +33,52 @@ feature -- Initialization feature -- Access - output: STRING_32 + output: STRING_GENERAL -- JSON representation - indentation: like output +feature -- Settings - indentation_step: like indentation + indentation_step: STRING + -- Text used for indentation. + --| by default a tabulation "%T" - line_number: INTEGER + object_count_inlining: INTEGER + -- Inline where object item count is under `object_count_inlining'. + --| ex 3: + --| { "a", "b", "c" } + --| ex 2: + --| { + --| "a", + --| "b", + --| "c" + --| } + + array_count_inlining: INTEGER + -- Inline where array item count is under `object_count_inlining'. + +feature -- Element change + + set_indentation_step (a_step: STRING) + -- Set `indentation_step' to `a_step'. + do + indentation_step := a_step + end + + set_object_count_inlining (a_nb: INTEGER) + -- Set `object_count_inlining' to `a_nb'. + do + object_count_inlining := a_nb + end + + set_array_count_inlining (a_nb: INTEGER) + -- Set `array_count_inlining' to `a_nb'. + do + array_count_inlining := a_nb + end + +feature {NONE} -- Implementation + + indentation: STRING indent do @@ -58,9 +97,7 @@ feature -- Access line_number := line_number + 1 end - object_count_inlining: INTEGER - - array_count_inlining: INTEGER + line_number: INTEGER feature -- Visitor Pattern @@ -71,10 +108,14 @@ feature -- Visitor Pattern l_json_array: ARRAYED_LIST [JSON_VALUE] l_line: like line_number l_multiple_lines: BOOLEAN + l_output: like output do + l_output := output l_json_array := a_json_array.array_representation - l_multiple_lines := l_json_array.count >= array_count_inlining or across l_json_array as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end - output.append ("[") + l_multiple_lines := l_json_array.count >= array_count_inlining + or across l_json_array as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end + l_output.append_code (91) -- '[' : 91 + l_line := line_number indent from @@ -89,14 +130,14 @@ feature -- Visitor Pattern value.accept (Current) l_json_array.forth if not l_json_array.after then - output.append (", ") + l_output.append (", ") end end exdent if line_number > l_line or l_json_array.count >= array_count_inlining then new_line end - output.append ("]") + l_output.append_code (93) -- ']' : 93 end visit_json_boolean (a_json_boolean: JSON_BOOLEAN) @@ -123,10 +164,12 @@ feature -- Visitor Pattern l_pairs: HASH_TABLE [JSON_VALUE, JSON_STRING] l_line: like line_number l_multiple_lines: BOOLEAN + l_output: like output do + l_output := output l_pairs := a_json_object.map_representation l_multiple_lines := l_pairs.count >= object_count_inlining or across l_pairs as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end - output.append ("{") + l_output.append_code (123) -- '{' : 123 l_line := line_number indent from @@ -138,26 +181,29 @@ feature -- Visitor Pattern new_line end l_pairs.key_for_iteration.accept (Current) - output.append (": ") + l_output.append (": ") l_pairs.item_for_iteration.accept (Current) l_pairs.forth if not l_pairs.after then - output.append (", ") + l_output.append (", ") end end exdent if line_number > l_line or l_pairs.count >= object_count_inlining then new_line end - output.append ("}") + l_output.append_code (125) -- '}' : 125 end visit_json_string (a_json_string: JSON_STRING) -- Visit `a_json_string'. + local + l_output: like output do - output.append ("%"") - output.append (a_json_string.item) - output.append ("%"") + l_output := output + l_output.append_code (34) -- '%"' : 34 + l_output.append (a_json_string.item) + l_output.append_code (34) -- '%"' : 34 end note diff --git a/package.iron b/package.iron new file mode 100644 index 00000000..90cfb9c9 --- /dev/null +++ b/package.iron @@ -0,0 +1,16 @@ +package json + +project + json_safe = "library/json-safe.ecf" + json = "library/json.ecf" + json_gobo_extension = "library/json_gobo_extension.ecf" + +note + title: Eiffel JSON + description: Eiffel JSON parser and visitors + tags: json,parser,text + license: MIT + copyright: Copyright (c) 2010-2014 Javier Velilla, Jocelyn Fiat and others, + link[github]: "project" https://github.com/eiffelhub/json + +end