diff options
50 files changed, 1259 insertions, 2055 deletions
diff --git a/.travis.yml b/.travis.yml index 17bde5d..70db3f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,15 +13,15 @@ rvm: - 2.2.2 - 2.2.3 - 2.2 + - 2.3.0 - ree - - rbx-2 - - jruby-18mode - - jruby-19mode - ruby-head matrix: + include: + - rvm: jruby + env: JRUBY_OPTS="--2.0" allow_failures: - rvm: rbx-2 - rvm: ruby-head script: "bundle exec rake" - sudo: false @@ -1,3 +1,12 @@ +2015-09-11 (1.8.5) + * There were still some mentions of dual GPL licensing in the source, but JSON + has just the Ruby license that itself includes an explicit dual-licensing + clause that allows covered software to be distributed under the terms of + the Simplified BSD License instead for all ruby versions >= 1.9.3. This is + however a GPL compatible license according to the Free Software Foundation. + I changed these mentions to be consistent with the Ruby license setting in + the gemspec files which were already correct now. +---------- (1.8.4) Skipped. 2015-06-01 (1.8.3) * Fix potential memory leak, thx to nobu. 2015-01-08 (1.8.2) diff --git a/COPYING b/COPYING deleted file mode 100644 index c3a2126..0000000 --- a/COPYING +++ /dev/null @@ -1,58 +0,0 @@ -Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.co.jp>. -You can redistribute it and/or modify it under either the terms of the GPL -(see GPL file), or the conditions below: - - 1. You may make and give away verbatim copies of the source form of the - software without restriction, provided that you duplicate all of the - original copyright notices and associated disclaimers. - - 2. You may modify your copy of the software in any way, provided that - you do at least ONE of the following: - - a) place your modifications in the Public Domain or otherwise - make them Freely Available, such as by posting said - modifications to Usenet or an equivalent medium, or by allowing - the author to include your modifications in the software. - - b) use the modified software only within your corporation or - organization. - - c) rename any non-standard executables so the names do not conflict - with standard executables, which must also be provided. - - d) make other distribution arrangements with the author. - - 3. You may distribute the software in object code or executable - form, provided that you do at least ONE of the following: - - a) distribute the executables and library files of the software, - together with instructions (in the manual page or equivalent) - on where to get the original distribution. - - b) accompany the distribution with the machine-readable source of - the software. - - c) give non-standard executables non-standard names, with - instructions on where to get the original software distribution. - - d) make other distribution arrangements with the author. - - 4. You may modify and include the part of the software into any other - software (possibly commercial). But some files in the distribution - are not written by the author, so that they are not under this terms. - - They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some - files under the ./missing directory. See each file for the copying - condition. - - 5. The scripts and library files supplied as input to or produced as - output from the software do not automatically fall under the - copyright of the software, but belong to whomever generated them, - and may be sold commercially, and may be aggregated with this - software. - - 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE. - diff --git a/COPYING-json-jruby b/COPYING-json-jruby deleted file mode 100644 index 137a3da..0000000 --- a/COPYING-json-jruby +++ /dev/null @@ -1,57 +0,0 @@ -JSON-JRuby is copyrighted free software by Daniel Luz <mernen at gmail dot com>, -and is a derivative work of Florian Frank's json library <flori at ping dot de>. -You can redistribute it and/or modify it under either the terms of the GPL -version 2 (see the file GPL), or the conditions below: - - 1. You may make and give away verbatim copies of the source form of the - software without restriction, provided that you duplicate all of the - original copyright notices and associated disclaimers. - - 2. You may modify your copy of the software in any way, provided that - you do at least ONE of the following: - - a) place your modifications in the Public Domain or otherwise - make them Freely Available, such as by posting said - modifications to Usenet or an equivalent medium, or by allowing - the author to include your modifications in the software. - - b) use the modified software only within your corporation or - organization. - - c) give non-standard binaries non-standard names, with - instructions on where to get the original software distribution. - - d) make other distribution arrangements with the author. - - 3. You may distribute the software in object code or binary form, - provided that you do at least ONE of the following: - - a) distribute the binaries and library files of the software, - together with instructions (in the manual page or equivalent) - on where to get the original distribution. - - b) accompany the distribution with the machine-readable source of - the software. - - c) give non-standard binaries non-standard names, with - instructions on where to get the original software distribution. - - d) make other distribution arrangements with the author. - - 4. You may modify and include the part of the software into any other - software (possibly commercial). But some files in the distribution - are not written by the author, so that they are not under these terms. - - For the list of those files and their copying conditions, see the - file LEGAL. - - 5. The scripts and library files supplied as input to or produced as - output from the software do not automatically fall under the - copyright of the software, but belong to whomever generated them, - and may be sold commercially, and may be aggregated with this - software. - - 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE. @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. @@ -6,5 +6,7 @@ gemspec :name => 'json' gemspec :name => 'json_pure' gemspec :name => 'json-java' -gem 'utils' +gem 'rake' +gem 'rdoc' gem 'test-unit' +gem 'byebug', :platform => :mri @@ -1,6 +1,6 @@ -= JSON implementation for Ruby {<img src="https://secure.travis-ci.org/flori/json.png" />}[http://travis-ci.org/flori/json] +# JSON implementation for Ruby {<img src="https://secure.travis-ci.org/flori/json.png" />}[http://travis-ci.org/flori/json] -== Description +## Description This is a implementation of the JSON specification according to RFC 4627 http://www.ietf.org/rfc/rfc4627.txt . Starting from version 1.0.0 on there @@ -11,17 +11,17 @@ will be two variants available: * The quite a bit faster native extension variant, which is in parts implemented in C or Java and comes with its own unicode conversion functions and a parser generated by the ragel state machine compiler - http://www.cs.queensu.ca/~thurston/ragel . + http://www.complang.org/ragel/ . Both variants of the JSON generator generate UTF-8 character sequences by -default. If an :ascii_only option with a true value is given, they escape all +default. If an :ascii\_only option with a true value is given, they escape all non-ASCII and control characters with \uXXXX escape sequences, and support UTF-16 surrogate pairs in order to be able to generate the whole range of unicode code points. All strings, that are to be encoded as JSON strings, should be UTF-8 byte sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8 -encoded, please use the to_json_raw_object method of String (which produces +encoded, please use the to\_json\_raw\_object method of String (which produces an object, that contains a byte array) and decode the result on the receiving endpoint. @@ -32,7 +32,7 @@ String#encoding set. If a document string has ASCII-8BIT as an encoding the parser attempts to figure out which of the UTF encodings from above it is and trys to parse it. -== Installation +## Installation It's recommended to use the extension variant of JSON, because it's faster than the pure ruby variant. If you cannot build it on your system, you can settle @@ -63,29 +63,18 @@ with: # gem install json_pure -== Compiling the extensions yourself - -If you want to build the extensions yourself you need rake: - - You can get it from rubyforge: - http://rubyforge.org/projects/rake - - or just type - - # gem install rake - - for the installation via rubygems. +## Compiling the extensions yourself If you want to create the parser.c file from its parser.rl file or draw nice -graphviz images of the state machines, you need ragel from: http://www.cs.queensu.ca/~thurston/ragel - +graphviz images of the state machines, you need ragel from: +http://www.complang.org/ragel/ -== Usage +## Usage To use JSON you can require 'json' to load the installed variant (either the extension 'json' or the pure -variant 'json_pure'). If you have installed the extension variant, you can +variant 'json\_pure'). If you have installed the extension variant, you can pick either the extension variant or the pure variant by typing require 'json/ext' or @@ -98,8 +87,8 @@ Now you can parse a JSON document into a ruby data structure by calling If you want to generate a JSON document from a ruby data structure call JSON.generate(data) -You can also use the pretty_generate method (which formats the output more -verbosely and nicely) or fast_generate (which doesn't do any of the security +You can also use the pretty\_generate method (which formats the output more +verbosely and nicely) or fast\_generate (which doesn't do any of the security checks generate performs, e. g. nesting deepness checks). To create a valid JSON document you have to make sure, that the output is @@ -134,7 +123,7 @@ To get the best compatibility to rails' JSON implementation, you can Both of the additions attempt to require 'json' (like above) first, if it has not been required yet. -== More Examples +## More Examples To create a JSON document from a ruby data structure, you can call JSON.generate like that: @@ -151,11 +140,11 @@ JSON.parse on it: Note, that the range from the original data structure is a simple string now. The reason for this is, that JSON doesn't support ranges or arbitrary classes. In this case the json library falls back to call -Object#to_json, which is the same as #to_s.to_json. +Object#to\_json, which is the same as #to\_s.to\_json. It's possible to add JSON support serialization to arbitrary classes by -simply implementing a more specialized version of the #to_json method, that -should return a JSON object (a hash converted to JSON with #to_json) like +simply implementing a more specialized version of the #to\_json method, that +should return a JSON object (a hash converted to JSON with #to\_json) like this (don't forget the *a for all the arguments): class Range @@ -167,15 +156,15 @@ this (don't forget the *a for all the arguments): end end -The hash key 'json_class' is the class, that will be asked to deserialise the +The hash key 'json\_class' is the class, that will be asked to deserialise the JSON representation later. In this case it's 'Range', but any namespace of the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be used to store the necessary data to configure the object to be deserialised. -If a the key 'json_class' is found in a JSON object, the JSON parser checks -if the given class responds to the json_create class method. If so, it is +If a the key 'json\_class' is found in a JSON object, the JSON parser checks +if the given class responds to the json\_create class method. If so, it is called with the JSON object converted to a Ruby hash. So a range can -be deserialised by implementing Range.json_create like this: +be deserialised by implementing Range.json\_create like this: class Range def self.json_create(o) @@ -193,7 +182,7 @@ Now it possible to serialise/deserialise ranges as well: JSON.generate always creates the shortest possible string representation of a ruby data structure in one line. This is good for data storage or network protocols, but not so good for humans to read. Fortunately there's also -JSON.pretty_generate (or JSON.pretty_generate) that creates a more readable +JSON.pretty\_generate (or JSON.pretty\_generate) that creates a more readable output: puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10]) @@ -217,14 +206,14 @@ output: ] There are also the methods Kernel#j for generate, and Kernel#jj for -pretty_generate output to the console, that work analogous to Core Ruby's p and +pretty\_generate output to the console, that work analogous to Core Ruby's p and the pp library's pp methods. The script tools/server.rb contains a small example if you want to test, how receiving a JSON object from a webrick server in your browser with the javasript prototype library http://www.prototypejs.org works. -== Speed Comparisons +## Speed Comparisons I have created some benchmark results (see the benchmarks/data-p4-3Ghz subdir of the package) for the JSON-parser to estimate the speed up in the C @@ -303,10 +292,10 @@ speed: In the table above 1-3 are JSON::Ext::Generator methods. 4, 6, and 7 are JSON::Pure::Generator methods and 5 is the Rails JSON generator. It is now a -bit faster than the generator_safe and generator_pretty methods of the pure +bit faster than the generator\_safe and generator\_pretty methods of the pure variant but slower than the others. -To achieve the fastest JSON document output, you can use the fast_generate +To achieve the fastest JSON document output, you can use the fast\_generate method. Beware, that this will disable the checking for circular Ruby data structures, which may cause JSON to go into an infinite loop. @@ -337,21 +326,19 @@ Here are the median comparisons for completeness' sake: calls/sec ( time) -> speed covers secs/call -== Author +## Author Florian Frank <mailto:flori@ping.de> -== License +## License -Ruby License, see the COPYING file included in the source distribution. The -Ruby License includes the GNU General Public License (GPL), Version 2, so see -the file GPL as well. +Ruby License, see https://www.ruby-lang.org/en/about/license.txt. -== Download +## Download The latest version of this library can be downloaded at -* http://rubyforge.org/frs?group_id=953 +* https://rubygems.org/gems/json Online Documentation should be located at @@ -23,10 +23,6 @@ class UndocumentedTestTask < Rake::TestTask def desc(*) end end -def skip_sdoc(src) - src.gsub(/^.*sdoc.*/) { |s| s + ' if RUBY_VERSION > "1.8.6"' } -end - MAKE = ENV['MAKE'] || %w[gmake make].find { |c| system(c, '-v') } BUNDLE = ENV['BUNDLE'] || %w[bundle].find { |c| system(c, '-v') } PKG_NAME = 'json' @@ -87,13 +83,12 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.files = PKG_FILES s.require_path = 'lib' - s.add_development_dependency 'permutation' - s.add_development_dependency 'sdoc', '~>0.3.16' - s.add_development_dependency 'rake', '~>0.9.2' + s.add_development_dependency 'rake' + s.add_development_dependency 'test-unit', '~> 2.0' - s.extra_rdoc_files << 'README.rdoc' + s.extra_rdoc_files << 'README.md' s.rdoc_options << - '--title' << 'JSON implemention for ruby' << '--main' << 'README.rdoc' + '--title' << 'JSON implemention for ruby' << '--main' << 'README.md' s.test_files.concat Dir['./tests/test_*.rb'] s.author = "Florian Frank" @@ -105,7 +100,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) desc 'Creates a json_pure.gemspec file' task :gemspec_pure => :version do File.open('json_pure.gemspec', 'w') do |gemspec| - gemspec.write skip_sdoc(spec_pure.to_ruby) + gemspec.write spec_pure.to_ruby end end @@ -125,12 +120,12 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.extensions = FileList['ext/**/extconf.rb'] s.require_path = 'lib' - s.add_development_dependency 'permutation' - s.add_development_dependency 'sdoc', '~>0.3.16' + s.add_development_dependency 'rake' + s.add_development_dependency 'test-unit', '~> 2.0' - s.extra_rdoc_files << 'README.rdoc' + s.extra_rdoc_files << 'README.md' s.rdoc_options << - '--title' << 'JSON implemention for Ruby' << '--main' << 'README.rdoc' + '--title' << 'JSON implemention for Ruby' << '--main' << 'README.md' s.test_files.concat Dir['./tests/test_*.rb'] s.author = "Florian Frank" @@ -142,7 +137,7 @@ if defined?(Gem) and defined?(Gem::PackageTask) desc 'Creates a json.gemspec file' task :gemspec_ext => :version do File.open('json.gemspec', 'w') do |gemspec| - gemspec.write skip_sdoc(spec_ext.to_ruby) + gemspec.write spec_ext.to_ruby end end @@ -173,13 +168,17 @@ EOT end end +task :check_env do + ENV.key?('JSON') or fail "JSON env var is required" +end + desc "Testing library (pure ruby)" -task :test_pure => [ :clean, :do_test_pure ] +task :test_pure => [ :clean, :check_env, :do_test_pure ] UndocumentedTestTask.new do |t| t.name = 'do_test_pure' - t.libs << 'lib' - t.test_files = FileList['tests/test_*.rb'] + t.libs << 'lib' << 'tests' + t.test_files = FileList['tests/*_test.rb'] t.verbose = true t.options = '-v' end @@ -257,12 +256,12 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' end desc "Testing library (jruby)" - task :test_ext => [ :create_jar, :do_test_ext ] + task :test_ext => [ :create_jar, :check_env, :do_test_ext ] UndocumentedTestTask.new do |t| t.name = 'do_test_ext' - t.libs << 'lib' - t.test_files = FileList['tests/test_*.rb'] + t.libs << 'lib' << 'tests' + t.test_files = FileList['tests/*_test.rb'] t.verbose = true t.options = '-v' end @@ -331,21 +330,16 @@ else end desc "Testing library (extension)" - task :test_ext => [ :compile, :do_test_ext ] + task :test_ext => [ :compile, :check_env, :do_test_ext ] UndocumentedTestTask.new do |t| t.name = 'do_test_ext' - t.libs << 'ext' << 'lib' - t.test_files = FileList['tests/test_*.rb'] + t.libs << 'ext' << 'lib' << 'tests' + t.test_files = FileList['tests/*_test.rb'] t.verbose = true t.options = '-v' end - desc "Create RDOC documentation" - task :doc => [ :version, EXT_PARSER_SRC ] do - sh "sdoc -o doc -t '#{PKG_TITLE}' -m README.rdoc README.rdoc lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}" - end - desc "Generate parser with ragel" task :ragel => EXT_PARSER_SRC @@ -1 +1 @@ -1.8.3 +2.0.0 diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 6300c64..393e29c 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -871,7 +871,7 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s } else { tmp = rb_funcall(obj, i_to_s, 0); Check_Type(tmp, T_STRING); - generate_json(buffer, Vstate, state, tmp); + generate_json_string(buffer, Vstate, state, tmp); } } @@ -915,21 +915,6 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj) } /* - * This function returns true if string is either a JSON array or JSON object. - * It might suffer from false positives, e. g. syntactically incorrect JSON in - * the string or certain UTF-8 characters on the right hand side. - */ -static int isArrayOrObject(VALUE string) -{ - long string_len = RSTRING_LEN(string); - char *p = RSTRING_PTR(string), *q = p + string_len - 1; - if (string_len < 2) return 0; - for (; p < q && isspace((unsigned char)*p); p++); - for (; q > p && isspace((unsigned char)*q); q--); - return (*p == '[' && *q == ']') || (*p == '{' && *q == '}'); -} - -/* * call-seq: generate(obj) * * Generates a valid JSON document from object +obj+ and returns the @@ -940,9 +925,6 @@ static VALUE cState_generate(VALUE self, VALUE obj) { VALUE result = cState_partial_generate(self, obj); GET_STATE(self); - if (!state->quirks_mode && !isArrayOrObject(result)) { - rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed"); - } return result; } diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 9c9d76a..c63a462 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -68,9 +68,9 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) } #ifdef HAVE_RUBY_ENCODING_H -static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE, - CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE; -static ID i_encoding, i_encode; +static VALUE CEncoding_UTF_8; + +static ID i_encode; #else static ID i_iconv; #endif @@ -79,7 +79,7 @@ static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode, + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, i_leftshift; @@ -89,11 +89,11 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, #line 92 "parser.c" -enum {JSON_object_start = 1}; -enum {JSON_object_first_final = 27}; -enum {JSON_object_error = 0}; +static const int JSON_object_start = 1; +static const int JSON_object_first_final = 27; +static const int JSON_object_error = 0; -enum {JSON_object_en_main = 1}; +static const int JSON_object_en_main = 1; #line 151 "parser.rl" @@ -467,11 +467,11 @@ case 26: #line 470 "parser.c" -enum {JSON_value_start = 1}; -enum {JSON_value_first_final = 21}; -enum {JSON_value_error = 0}; +static const int JSON_value_start = 1; +static const int JSON_value_first_final = 29; +static const int JSON_value_error = 0; -enum {JSON_value_en_main = 1}; +static const int JSON_value_en_main = 1; #line 271 "parser.rl" @@ -495,40 +495,49 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul goto _test_eof; switch ( cs ) { +st1: + if ( ++p == pe ) + goto _test_eof1; case 1: switch( (*p) ) { - case 34: goto tr0; - case 45: goto tr2; - case 73: goto st2; - case 78: goto st9; - case 91: goto tr5; - case 102: goto st11; - case 110: goto st15; - case 116: goto st18; - case 123: goto tr9; + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr3; + case 47: goto st6; + case 73: goto st10; + case 78: goto st17; + case 91: goto tr7; + case 102: goto st19; + case 110: goto st23; + case 116: goto st26; + case 123: goto tr11; } - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr2; + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3; + } else if ( (*p) >= 9 ) + goto st1; goto st0; st0: cs = 0; goto _out; -tr0: +tr2: #line 219 "parser.rl" { char *np = JSON_parse_string(json, p, pe, result); - if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;} + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } - goto st21; -tr2: + goto st29; +tr3: #line 224 "parser.rl" { char *np; - if(pe > p + 9 - json->quirks_mode && !strncmp(MinusInfinity, p, 9)) { + if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { if (json->allow_nan) { *result = CMinusInfinity; {p = (( p + 10))-1;} - p--; {p++; cs = 21; goto _out;} + p--; {p++; cs = 29; goto _out;} } else { rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); } @@ -537,30 +546,30 @@ tr2: if (np != NULL) {p = (( np))-1;} np = JSON_parse_integer(json, p, pe, result); if (np != NULL) {p = (( np))-1;} - p--; {p++; cs = 21; goto _out;} + p--; {p++; cs = 29; goto _out;} } - goto st21; -tr5: + goto st29; +tr7: #line 242 "parser.rl" { char *np; json->current_nesting++; np = JSON_parse_array(json, p, pe, result); json->current_nesting--; - if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;} + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } - goto st21; -tr9: + goto st29; +tr11: #line 250 "parser.rl" { char *np; json->current_nesting++; np = JSON_parse_object(json, p, pe, result); json->current_nesting--; - if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;} + if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;} } - goto st21; -tr16: + goto st29; +tr25: #line 212 "parser.rl" { if (json->allow_nan) { @@ -569,8 +578,8 @@ tr16: rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8); } } - goto st21; -tr18: + goto st29; +tr27: #line 205 "parser.rl" { if (json->allow_nan) { @@ -579,168 +588,240 @@ tr18: rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2); } } - goto st21; -tr22: + goto st29; +tr31: #line 199 "parser.rl" { *result = Qfalse; } - goto st21; -tr25: + goto st29; +tr34: #line 196 "parser.rl" { *result = Qnil; } - goto st21; -tr28: + goto st29; +tr37: #line 202 "parser.rl" { *result = Qtrue; } - goto st21; -st21: + goto st29; +st29: if ( ++p == pe ) - goto _test_eof21; -case 21: + goto _test_eof29; +case 29: #line 258 "parser.rl" - { p--; {p++; cs = 21; goto _out;} } -#line 608 "parser.c" + { p--; {p++; cs = 29; goto _out;} } +#line 617 "parser.c" + switch( (*p) ) { + case 13: goto st29; + case 32: goto st29; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st29; goto st0; st2: if ( ++p == pe ) goto _test_eof2; case 2: - if ( (*p) == 110 ) - goto st3; + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } goto st0; st3: if ( ++p == pe ) goto _test_eof3; case 3: - if ( (*p) == 102 ) + if ( (*p) == 42 ) goto st4; - goto st0; + goto st3; st4: if ( ++p == pe ) goto _test_eof4; case 4: - if ( (*p) == 105 ) - goto st5; - goto st0; + switch( (*p) ) { + case 42: goto st4; + case 47: goto st29; + } + goto st3; st5: if ( ++p == pe ) goto _test_eof5; case 5: - if ( (*p) == 110 ) - goto st6; - goto st0; + if ( (*p) == 10 ) + goto st29; + goto st5; st6: if ( ++p == pe ) goto _test_eof6; case 6: - if ( (*p) == 105 ) - goto st7; + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } goto st0; st7: if ( ++p == pe ) goto _test_eof7; case 7: - if ( (*p) == 116 ) + if ( (*p) == 42 ) goto st8; - goto st0; + goto st7; st8: if ( ++p == pe ) goto _test_eof8; case 8: - if ( (*p) == 121 ) - goto tr16; - goto st0; + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; st9: if ( ++p == pe ) goto _test_eof9; case 9: - if ( (*p) == 97 ) - goto st10; - goto st0; + if ( (*p) == 10 ) + goto st1; + goto st9; st10: if ( ++p == pe ) goto _test_eof10; case 10: - if ( (*p) == 78 ) - goto tr18; + if ( (*p) == 110 ) + goto st11; goto st0; st11: if ( ++p == pe ) goto _test_eof11; case 11: - if ( (*p) == 97 ) + if ( (*p) == 102 ) goto st12; goto st0; st12: if ( ++p == pe ) goto _test_eof12; case 12: - if ( (*p) == 108 ) + if ( (*p) == 105 ) goto st13; goto st0; st13: if ( ++p == pe ) goto _test_eof13; case 13: - if ( (*p) == 115 ) + if ( (*p) == 110 ) goto st14; goto st0; st14: if ( ++p == pe ) goto _test_eof14; case 14: - if ( (*p) == 101 ) - goto tr22; + if ( (*p) == 105 ) + goto st15; goto st0; st15: if ( ++p == pe ) goto _test_eof15; case 15: - if ( (*p) == 117 ) + if ( (*p) == 116 ) goto st16; goto st0; st16: if ( ++p == pe ) goto _test_eof16; case 16: - if ( (*p) == 108 ) - goto st17; + if ( (*p) == 121 ) + goto tr25; goto st0; st17: if ( ++p == pe ) goto _test_eof17; case 17: - if ( (*p) == 108 ) - goto tr25; + if ( (*p) == 97 ) + goto st18; goto st0; st18: if ( ++p == pe ) goto _test_eof18; case 18: - if ( (*p) == 114 ) - goto st19; + if ( (*p) == 78 ) + goto tr27; goto st0; st19: if ( ++p == pe ) goto _test_eof19; case 19: - if ( (*p) == 117 ) + if ( (*p) == 97 ) goto st20; goto st0; st20: if ( ++p == pe ) goto _test_eof20; case 20: + if ( (*p) == 108 ) + goto st21; + goto st0; +st21: + if ( ++p == pe ) + goto _test_eof21; +case 21: + if ( (*p) == 115 ) + goto st22; + goto st0; +st22: + if ( ++p == pe ) + goto _test_eof22; +case 22: if ( (*p) == 101 ) - goto tr28; + goto tr31; + goto st0; +st23: + if ( ++p == pe ) + goto _test_eof23; +case 23: + if ( (*p) == 117 ) + goto st24; + goto st0; +st24: + if ( ++p == pe ) + goto _test_eof24; +case 24: + if ( (*p) == 108 ) + goto st25; + goto st0; +st25: + if ( ++p == pe ) + goto _test_eof25; +case 25: + if ( (*p) == 108 ) + goto tr34; + goto st0; +st26: + if ( ++p == pe ) + goto _test_eof26; +case 26: + if ( (*p) == 114 ) + goto st27; + goto st0; +st27: + if ( ++p == pe ) + goto _test_eof27; +case 27: + if ( (*p) == 117 ) + goto st28; + goto st0; +st28: + if ( ++p == pe ) + goto _test_eof28; +case 28: + if ( (*p) == 101 ) + goto tr37; goto st0; } - _test_eof21: cs = 21; goto _test_eof; + _test_eof1: cs = 1; goto _test_eof; + _test_eof29: cs = 29; goto _test_eof; _test_eof2: cs = 2; goto _test_eof; _test_eof3: cs = 3; goto _test_eof; _test_eof4: cs = 4; goto _test_eof; @@ -760,6 +841,14 @@ case 20: _test_eof18: cs = 18; goto _test_eof; _test_eof19: cs = 19; goto _test_eof; _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof28: cs = 28; goto _test_eof; _test_eof: {} _out: {} @@ -775,12 +864,12 @@ case 20: } -#line 779 "parser.c" -enum {JSON_integer_start = 1}; -enum {JSON_integer_first_final = 3}; -enum {JSON_integer_error = 0}; +#line 868 "parser.c" +static const int JSON_integer_start = 1; +static const int JSON_integer_first_final = 3; +static const int JSON_integer_error = 0; -enum {JSON_integer_en_main = 1}; +static const int JSON_integer_en_main = 1; #line 295 "parser.rl" @@ -791,7 +880,7 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res int cs = EVIL; -#line 795 "parser.c" +#line 884 "parser.c" { cs = JSON_integer_start; } @@ -799,7 +888,7 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res #line 302 "parser.rl" json->memo = p; -#line 803 "parser.c" +#line 892 "parser.c" { if ( p == pe ) goto _test_eof; @@ -840,7 +929,7 @@ st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 844 "parser.c" +#line 933 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -874,12 +963,12 @@ case 5: } -#line 878 "parser.c" -enum {JSON_float_start = 1}; -enum {JSON_float_first_final = 8}; -enum {JSON_float_error = 0}; +#line 967 "parser.c" +static const int JSON_float_start = 1; +static const int JSON_float_first_final = 8; +static const int JSON_float_error = 0; -enum {JSON_float_en_main = 1}; +static const int JSON_float_en_main = 1; #line 329 "parser.rl" @@ -890,7 +979,7 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 894 "parser.c" +#line 983 "parser.c" { cs = JSON_float_start; } @@ -898,7 +987,7 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul #line 336 "parser.rl" json->memo = p; -#line 902 "parser.c" +#line 991 "parser.c" { if ( p == pe ) goto _test_eof; @@ -963,7 +1052,7 @@ st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 967 "parser.c" +#line 1056 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -1040,12 +1129,12 @@ case 7: -#line 1044 "parser.c" -enum {JSON_array_start = 1}; -enum {JSON_array_first_final = 17}; -enum {JSON_array_error = 0}; +#line 1133 "parser.c" +static const int JSON_array_start = 1; +static const int JSON_array_first_final = 17; +static const int JSON_array_error = 0; -enum {JSON_array_en_main = 1}; +static const int JSON_array_en_main = 1; #line 381 "parser.rl" @@ -1062,14 +1151,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); -#line 1066 "parser.c" +#line 1155 "parser.c" { cs = JSON_array_start; } #line 394 "parser.rl" -#line 1073 "parser.c" +#line 1162 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1128,7 +1217,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1132 "parser.c" +#line 1221 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -1235,7 +1324,7 @@ st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 1239 "parser.c" +#line 1328 "parser.c" goto st0; st13: if ( ++p == pe ) @@ -1372,12 +1461,12 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) } -#line 1376 "parser.c" -enum {JSON_string_start = 1}; -enum {JSON_string_first_final = 8}; -enum {JSON_string_error = 0}; +#line 1465 "parser.c" +static const int JSON_string_start = 1; +static const int JSON_string_first_final = 8; +static const int JSON_string_error = 0; -enum {JSON_string_en_main = 1}; +static const int JSON_string_en_main = 1; #line 494 "parser.rl" @@ -1402,7 +1491,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = rb_str_buf_new(0); -#line 1406 "parser.c" +#line 1495 "parser.c" { cs = JSON_string_start; } @@ -1410,7 +1499,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu #line 515 "parser.rl" json->memo = p; -#line 1414 "parser.c" +#line 1503 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1453,7 +1542,7 @@ st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 1457 "parser.c" +#line 1546 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -1566,40 +1655,9 @@ case 7: static VALUE convert_encoding(VALUE source) { - char *ptr = RSTRING_PTR(source); - long len = RSTRING_LEN(source); - if (len < 2) { - rb_raise(eParserError, "A JSON text must at least contain two octets!"); - } #ifdef HAVE_RUBY_ENCODING_H { - VALUE encoding = rb_funcall(source, i_encoding, 0); - if (encoding == CEncoding_ASCII_8BIT) { - if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32BE); - } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16BE); - } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32LE); - } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16LE); - } else { - source = rb_str_dup(source); - FORCE_UTF8(source); - } - } else { - source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8); - } - } -#else - if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source); - } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source); - } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source); - } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source); + source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8); } #endif return source; @@ -1623,8 +1681,9 @@ static VALUE convert_encoding(VALUE source) * defiance of RFC 4627 to be parsed by the Parser. This option defaults to * false. * * *symbolize_names*: If set to true, returns symbols for the names - * (keys) in a JSON object. Otherwise strings are returned, which is also - * the default. + * (keys) in a JSON object. Otherwise strings are returned, which is + * also the default. It's not possible to use this option in + * conjunction with the *create_additions* option. * * *create_additions*: If set to false, the Parser doesn't create * additions even if a matching class and create_id was found. This option * defaults to false. @@ -1669,19 +1728,17 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } - tmp = ID2SYM(i_quirks_mode); - if (option_given_p(opts, tmp)) { - VALUE quirks_mode = rb_hash_aref(opts, tmp); - json->quirks_mode = RTEST(quirks_mode) ? 1 : 0; - } else { - json->quirks_mode = 0; - } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); } else { json->create_additions = 0; } + if (json->symbolize_names && json->create_additions) { + rb_raise(rb_eArgError, + "options :symbolize_names and :create_additions cannot be " + " used in conjunction"); + } tmp = ID2SYM(i_create_id); if (option_given_p(opts, tmp)) { json->create_id = rb_hash_aref(opts, tmp); @@ -1717,9 +1774,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->array_class = Qnil; } source = rb_convert_type(source, T_STRING, "String", "to_str"); - if (!json->quirks_mode) { - source = convert_encoding(StringValue(source)); - } + source = convert_encoding(StringValue(source)); json->current_nesting = 0; StringValue(source); json->len = RSTRING_LEN(source); @@ -1729,209 +1784,41 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } -#line 1733 "parser.c" -enum {JSON_start = 1}; -enum {JSON_first_final = 10}; -enum {JSON_error = 0}; +#line 1788 "parser.c" +static const int JSON_start = 1; +static const int JSON_first_final = 10; +static const int JSON_error = 0; -enum {JSON_en_main = 1}; +static const int JSON_en_main = 1; -#line 740 "parser.rl" +#line 696 "parser.rl" -static VALUE cParser_parse_strict(VALUE self) +/* + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + */ +static VALUE cParser_parse(VALUE self) { - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; -#line 1752 "parser.c" +#line 1813 "parser.c" { cs = JSON_start; } -#line 750 "parser.rl" - p = json->source; - pe = p + json->len; - -#line 1761 "parser.c" - { - if ( p == pe ) - goto _test_eof; - switch ( cs ) - { -st1: - if ( ++p == pe ) - goto _test_eof1; -case 1: - switch( (*p) ) { - case 13: goto st1; - case 32: goto st1; - case 47: goto st2; - case 91: goto tr3; - case 123: goto tr4; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st1; - goto st0; -st0: -cs = 0; - goto _out; -st2: - if ( ++p == pe ) - goto _test_eof2; -case 2: - switch( (*p) ) { - case 42: goto st3; - case 47: goto st5; - } - goto st0; -st3: - if ( ++p == pe ) - goto _test_eof3; -case 3: - if ( (*p) == 42 ) - goto st4; - goto st3; -st4: - if ( ++p == pe ) - goto _test_eof4; -case 4: - switch( (*p) ) { - case 42: goto st4; - case 47: goto st1; - } - goto st3; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: - if ( (*p) == 10 ) - goto st1; - goto st5; -tr3: -#line 729 "parser.rl" - { - char *np; - json->current_nesting = 1; - np = JSON_parse_array(json, p, pe, &result); - if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} - } - goto st10; -tr4: -#line 722 "parser.rl" - { - char *np; - json->current_nesting = 1; - np = JSON_parse_object(json, p, pe, &result); - if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} - } - goto st10; -st10: - if ( ++p == pe ) - goto _test_eof10; -case 10: -#line 1838 "parser.c" - switch( (*p) ) { - case 13: goto st10; - case 32: goto st10; - case 47: goto st6; - } - if ( 9 <= (*p) && (*p) <= 10 ) - goto st10; - goto st0; -st6: - if ( ++p == pe ) - goto _test_eof6; -case 6: - switch( (*p) ) { - case 42: goto st7; - case 47: goto st9; - } - goto st0; -st7: - if ( ++p == pe ) - goto _test_eof7; -case 7: - if ( (*p) == 42 ) - goto st8; - goto st7; -st8: - if ( ++p == pe ) - goto _test_eof8; -case 8: - switch( (*p) ) { - case 42: goto st8; - case 47: goto st10; - } - goto st7; -st9: - if ( ++p == pe ) - goto _test_eof9; -case 9: - if ( (*p) == 10 ) - goto st10; - goto st9; - } - _test_eof1: cs = 1; goto _test_eof; - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - - _test_eof: {} - _out: {} - } - -#line 753 "parser.rl" +#line 712 "parser.rl" + p = json->source; + pe = p + json->len; - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); - return Qnil; - } -} - - - -#line 1907 "parser.c" -enum {JSON_quirks_mode_start = 1}; -enum {JSON_quirks_mode_first_final = 10}; -enum {JSON_quirks_mode_error = 0}; - -enum {JSON_quirks_mode_en_main = 1}; - - -#line 778 "parser.rl" - - -static VALUE cParser_parse_quirks_mode(VALUE self) -{ - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - -#line 1926 "parser.c" - { - cs = JSON_quirks_mode_start; - } - -#line 788 "parser.rl" - p = json->source; - pe = p + json->len; - -#line 1935 "parser.c" +#line 1822 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1965,7 +1852,7 @@ st0: cs = 0; goto _out; tr2: -#line 770 "parser.rl" +#line 688 "parser.rl" { char *np = JSON_parse_value(json, p, pe, &result); if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} @@ -1975,7 +1862,7 @@ st10: if ( ++p == pe ) goto _test_eof10; case 10: -#line 1979 "parser.c" +#line 1866 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -2064,30 +1951,13 @@ case 9: _out: {} } -#line 791 "parser.rl" +#line 715 "parser.rl" - if (cs >= JSON_quirks_mode_first_final && p == pe) { - return result; - } else { - rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); - return Qnil; - } -} - -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - */ -static VALUE cParser_parse(VALUE self) -{ - GET_PARSER; - - if (json->quirks_mode) { - return cParser_parse_quirks_mode(self); + if (cs >= JSON_first_final && p == pe) { + return result; } else { - return cParser_parse_strict(self); + rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; } } @@ -2145,18 +2015,6 @@ static VALUE cParser_source(VALUE self) return rb_str_dup(json->Vsource); } -/* - * call-seq: quirks_mode?() - * - * Returns a true, if this parser is in quirks_mode, false otherwise. - */ -static VALUE cParser_quirks_mode_p(VALUE self) -{ - GET_PARSER; - return json->quirks_mode ? Qtrue : Qfalse; -} - - void Init_parser(void) { rb_require("json/common"); @@ -2169,7 +2027,6 @@ void Init_parser(void) rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); - rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); @@ -2183,7 +2040,6 @@ void Init_parser(void) i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); - i_quirks_mode = rb_intern("quirks_mode"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_match = rb_intern("match"); @@ -2195,12 +2051,6 @@ void Init_parser(void) i_leftshift = rb_intern("<<"); #ifdef HAVE_RUBY_ENCODING_H CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); - CEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be")); - CEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le")); - CEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be")); - CEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le")); - CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit")); - i_encoding = rb_intern("encoding"); i_encode = rb_intern("encode"); #else i_iconv = rb_intern("iconv"); diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h index abcc257..ec26e85 100644 --- a/ext/json/ext/parser/parser.h +++ b/ext/json/ext/parser/parser.h @@ -38,7 +38,6 @@ typedef struct JSON_ParserStruct { int allow_nan; int parsing_name; int symbolize_names; - int quirks_mode; VALUE object_class; VALUE array_class; int create_additions; diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index 216ad26..6f73307 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -66,9 +66,9 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) } #ifdef HAVE_RUBY_ENCODING_H -static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE, - CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE; -static ID i_encoding, i_encode; +static VALUE CEncoding_UTF_8; + +static ID i_encode; #else static ID i_iconv; #endif @@ -77,7 +77,7 @@ static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode, + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, i_leftshift; @@ -223,7 +223,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu action parse_number { char *np; - if(pe > fpc + 9 - json->quirks_mode && !strncmp(MinusInfinity, fpc, 9)) { + if(pe > fpc + 8 && !strncmp(MinusInfinity, fpc, 9)) { if (json->allow_nan) { *result = CMinusInfinity; fexec p + 10; @@ -257,7 +257,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu action exit { fhold; fbreak; } -main := ( +main := ignore* ( Vnull @parse_null | Vfalse @parse_false | Vtrue @parse_true | @@ -267,7 +267,7 @@ main := ( begin_string >parse_string | begin_array >parse_array | begin_object >parse_object - ) %*exit; + ) ignore* %*exit; }%% static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -550,40 +550,9 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu static VALUE convert_encoding(VALUE source) { - char *ptr = RSTRING_PTR(source); - long len = RSTRING_LEN(source); - if (len < 2) { - rb_raise(eParserError, "A JSON text must at least contain two octets!"); - } #ifdef HAVE_RUBY_ENCODING_H { - VALUE encoding = rb_funcall(source, i_encoding, 0); - if (encoding == CEncoding_ASCII_8BIT) { - if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32BE); - } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16BE); - } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32LE); - } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) { - source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16LE); - } else { - source = rb_str_dup(source); - FORCE_UTF8(source); - } - } else { - source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8); - } - } -#else - if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source); - } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source); - } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source); - } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) { - source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source); + source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8); } #endif return source; @@ -607,8 +576,9 @@ static VALUE convert_encoding(VALUE source) * defiance of RFC 4627 to be parsed by the Parser. This option defaults to * false. * * *symbolize_names*: If set to true, returns symbols for the names - * (keys) in a JSON object. Otherwise strings are returned, which is also - * the default. + * (keys) in a JSON object. Otherwise strings are returned, which is + * also the default. It's not possible to use this option in + * conjunction with the *create_additions* option. * * *create_additions*: If set to false, the Parser doesn't create * additions even if a matching class and create_id was found. This option * defaults to false. @@ -653,19 +623,17 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } - tmp = ID2SYM(i_quirks_mode); - if (option_given_p(opts, tmp)) { - VALUE quirks_mode = rb_hash_aref(opts, tmp); - json->quirks_mode = RTEST(quirks_mode) ? 1 : 0; - } else { - json->quirks_mode = 0; - } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); } else { json->create_additions = 0; } + if (json->symbolize_names && json->create_additions) { + rb_raise(rb_eArgError, + "options :symbolize_names and :create_additions cannot be " + " used in conjunction"); + } tmp = ID2SYM(i_create_id); if (option_given_p(opts, tmp)) { json->create_id = rb_hash_aref(opts, tmp); @@ -701,9 +669,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->array_class = Qnil; } source = rb_convert_type(source, T_STRING, "String", "to_str"); - if (!json->quirks_mode) { - source = convert_encoding(StringValue(source)); - } + source = convert_encoding(StringValue(source)); json->current_nesting = 0; StringValue(source); json->len = RSTRING_LEN(source); @@ -719,54 +685,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) include JSON_common; - action parse_object { - char *np; - json->current_nesting = 1; - np = JSON_parse_object(json, fpc, pe, &result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action parse_array { - char *np; - json->current_nesting = 1; - np = JSON_parse_array(json, fpc, pe, &result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - main := ignore* ( - begin_object >parse_object | - begin_array >parse_array - ) ignore*; -}%% - -static VALUE cParser_parse_strict(VALUE self) -{ - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - %% write init; - p = json->source; - pe = p + json->len; - %% write exec; - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); - return Qnil; - } -} - - -%%{ - machine JSON_quirks_mode; - - write data; - - include JSON_common; - action parse_value { char *np = JSON_parse_value(json, fpc, pe, &result); if (np == NULL) { fhold; fbreak; } else fexec np; @@ -777,26 +695,6 @@ static VALUE cParser_parse_strict(VALUE self) ) ignore*; }%% -static VALUE cParser_parse_quirks_mode(VALUE self) -{ - char *p, *pe; - int cs = EVIL; - VALUE result = Qnil; - GET_PARSER; - - %% write init; - p = json->source; - pe = p + json->len; - %% write exec; - - if (cs >= JSON_quirks_mode_first_final && p == pe) { - return result; - } else { - rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); - return Qnil; - } -} - /* * call-seq: parse() * @@ -805,12 +703,21 @@ static VALUE cParser_parse_quirks_mode(VALUE self) */ static VALUE cParser_parse(VALUE self) { + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; GET_PARSER; - if (json->quirks_mode) { - return cParser_parse_quirks_mode(self); + %% write init; + p = json->source; + pe = p + json->len; + %% write exec; + + if (cs >= JSON_first_final && p == pe) { + return result; } else { - return cParser_parse_strict(self); + rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; } } @@ -868,18 +775,6 @@ static VALUE cParser_source(VALUE self) return rb_str_dup(json->Vsource); } -/* - * call-seq: quirks_mode?() - * - * Returns a true, if this parser is in quirks_mode, false otherwise. - */ -static VALUE cParser_quirks_mode_p(VALUE self) -{ - GET_PARSER; - return json->quirks_mode ? Qtrue : Qfalse; -} - - void Init_parser(void) { rb_require("json/common"); @@ -892,7 +787,6 @@ void Init_parser(void) rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); - rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); @@ -906,7 +800,6 @@ void Init_parser(void) i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); - i_quirks_mode = rb_intern("quirks_mode"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_match = rb_intern("match"); @@ -918,12 +811,6 @@ void Init_parser(void) i_leftshift = rb_intern("<<"); #ifdef HAVE_RUBY_ENCODING_H CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); - CEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be")); - CEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le")); - CEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be")); - CEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le")); - CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit")); - i_encoding = rb_intern("encoding"); i_encode = rb_intern("encode"); #else i_iconv = rb_intern("iconv"); diff --git a/ext/json/extconf.rb b/ext/json/extconf.rb index 850798c..7595d58 100644 --- a/ext/json/extconf.rb +++ b/ext/json/extconf.rb @@ -1,3 +1,2 @@ require 'mkmf' create_makefile('json') - diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java index ed9e54b..6f6ab66 100644 --- a/java/src/json/ext/ByteListTranscoder.java +++ b/java/src/json/ext/ByteListTranscoder.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index ecceb27..bb3a394 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; @@ -428,11 +427,14 @@ public final class Generator { new Handler<IRubyObject>() { @Override RubyString generateNew(Session session, IRubyObject object) { - IRubyObject result = - object.callMethod(session.getContext(), "to_json", - new IRubyObject[] {session.getState()}); - if (result instanceof RubyString) return (RubyString)result; - throw session.getRuntime().newTypeError("to_json must return a String"); + if (object.respondsTo("to_json")) { + IRubyObject result = object.callMethod(session.getContext(), "to_json", + new IRubyObject[] {session.getState()}); + if (result instanceof RubyString) return (RubyString)result; + throw session.getRuntime().newTypeError("to_json must return a String"); + } else { + return OBJECT_HANDLER.generateNew(session, object); + } } @Override diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java index 637b579..bde7a18 100644 --- a/java/src/json/ext/GeneratorMethods.java +++ b/java/src/json/ext/GeneratorMethods.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/GeneratorService.java b/java/src/json/ext/GeneratorService.java index ed33639..e665ad1 100644 --- a/java/src/json/ext/GeneratorService.java +++ b/java/src/json/ext/GeneratorService.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 3065307..11d98d0 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; @@ -208,10 +207,6 @@ public class GeneratorState extends RubyObject { @JRubyMethod public IRubyObject generate(ThreadContext context, IRubyObject obj) { RubyString result = Generator.generateJson(context, obj, this); - if (!quirksMode && !objectOrArrayLiteral(result)) { - throw Utils.newException(context, Utils.M_GENERATOR_ERROR, - "only generation of JSON objects or arrays allowed"); - } RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); if (info.encodingsSupported()) { result.force_encoding(context, info.utf8.get()); @@ -219,34 +214,6 @@ public class GeneratorState extends RubyObject { return result; } - /** - * Ensures the given string is in the form "[...]" or "{...}", being - * possibly surrounded by white space. - * The string's encoding must be ASCII-compatible. - * @param value - * @return - */ - private static boolean objectOrArrayLiteral(RubyString value) { - ByteList bl = value.getByteList(); - int len = bl.length(); - - for (int pos = 0; pos < len - 1; pos++) { - int b = bl.get(pos); - if (Character.isWhitespace(b)) continue; - - // match the opening brace - switch (b) { - case '[': - return matchClosingBrace(bl, pos, len, ']'); - case '{': - return matchClosingBrace(bl, pos, len, '}'); - default: - return false; - } - } - return false; - } - private static boolean matchClosingBrace(ByteList bl, int pos, int len, int brace) { for (int endPos = len - 1; endPos > pos; endPos--) { diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java index 8212503..9bb6e64 100644 --- a/java/src/json/ext/OptionsReader.java +++ b/java/src/json/ext/OptionsReader.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 37423f5..5458fb1 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -3,8 +3,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index 6d65963..d43c74f 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/ParserService.java b/java/src/json/ext/ParserService.java index dde8834..b6015f9 100644 --- a/java/src/json/ext/ParserService.java +++ b/java/src/json/ext/ParserService.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java index 5de5740..ceaca5b 100644 --- a/java/src/json/ext/RuntimeInfo.java +++ b/java/src/json/ext/RuntimeInfo.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/StringDecoder.java b/java/src/json/ext/StringDecoder.java index 6023113..76cf183 100644 --- a/java/src/json/ext/StringDecoder.java +++ b/java/src/json/ext/StringDecoder.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index 57bd19b..9d40dd3 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -1,3 +1,8 @@ +/* + * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. + * + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt + */ package json.ext; import org.jruby.exceptions.RaiseException; diff --git a/java/src/json/ext/Utils.java b/java/src/json/ext/Utils.java index 44d6a55..ed6f832 100644 --- a/java/src/json/ext/Utils.java +++ b/java/src/json/ext/Utils.java @@ -1,8 +1,7 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. + * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt */ package json.ext; diff --git a/json-java.gemspec b/json-java.gemspec index 14864f8..1524b1f 100644 --- a/json-java.gemspec +++ b/json-java.gemspec @@ -14,6 +14,21 @@ spec = Gem::Specification.new do |s| s.licenses = ["Ruby"] s.files = Dir["{docs,lib,tests}/**/*"] + + if s.respond_to? :specification_version then + s.specification_version = 4 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_development_dependency(%q<rake>, [">= 0"]) + s.add_development_dependency(%q<test-unit>, ["~> 2.0"]) + else + s.add_dependency(%q<rake>, [">= 0"]) + s.add_dependency(%q<test-unit>, ["~> 2.0"]) + end + else + s.add_dependency(%q<rake>, [">= 0"]) + s.add_dependency(%q<test-unit>, ["~> 2.0"]) + end end if $0 == __FILE__ diff --git a/json.gemspec b/json.gemspec Binary files differindex 0b40989..a9493ff 100644 --- a/json.gemspec +++ b/json.gemspec diff --git a/json_pure.gemspec b/json_pure.gemspec index 3631d3c..13c8e8b 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -1,40 +1,37 @@ # -*- encoding: utf-8 -*- -# stub: json_pure 1.8.3 ruby lib +# stub: json_pure 2.0.0 ruby lib Gem::Specification.new do |s| s.name = "json_pure" - s.version = "1.8.3" + s.version = "2.0.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Florian Frank"] - s.date = "2015-06-01" + s.date = "2016-02-25" s.description = "This is a JSON implementation in pure Ruby." s.email = "flori@ping.de" - s.extra_rdoc_files = ["README.rdoc"] - s.files = ["./tests/test_json.rb", "./tests/test_json_addition.rb", "./tests/test_json_encoding.rb", "./tests/test_json_fixtures.rb", "./tests/test_json_generate.rb", "./tests/test_json_generic_object.rb", "./tests/test_json_string_matching.rb", "./tests/test_json_unicode.rb", ".gitignore", ".travis.yml", "CHANGES", "COPYING", "COPYING-json-jruby", "GPL", "Gemfile", "README-json-jruby.markdown", "README.rdoc", "Rakefile", "TODO", "VERSION", "data/example.json", "data/index.html", "data/prototype.js", "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", "ext/json/ext/generator/generator.c", "ext/json/ext/generator/generator.h", "ext/json/ext/parser/depend", "ext/json/ext/parser/extconf.rb", "ext/json/ext/parser/parser.c", "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", "install.rb", "java/src/json/ext/ByteListTranscoder.java", "java/src/json/ext/Generator.java", "java/src/json/ext/GeneratorMethods.java", "java/src/json/ext/GeneratorService.java", "java/src/json/ext/GeneratorState.java", "java/src/json/ext/OptionsReader.java", "java/src/json/ext/Parser.java", "java/src/json/ext/Parser.rl", "java/src/json/ext/ParserService.java", "java/src/json/ext/RuntimeInfo.java", "java/src/json/ext/StringDecoder.java", "java/src/json/ext/StringEncoder.java", "java/src/json/ext/Utils.java", "json-java.gemspec", "json.gemspec", "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", "lib/json/add/core.rb", "lib/json/add/date.rb", "lib/json/add/date_time.rb", "lib/json/add/exception.rb", "lib/json/add/ostruct.rb", "lib/json/add/range.rb", "lib/json/add/rational.rb", "lib/json/add/regexp.rb", "lib/json/add/struct.rb", "lib/json/add/symbol.rb", "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", "tests/fixtures/fail1.json", "tests/fixtures/fail10.json", "tests/fixtures/fail11.json", "tests/fixtures/fail12.json", "tests/fixtures/fail13.json", "tests/fixtures/fail14.json", "tests/fixtures/fail18.json", "tests/fixtures/fail19.json", "tests/fixtures/fail2.json", "tests/fixtures/fail20.json", "tests/fixtures/fail21.json", "tests/fixtures/fail22.json", "tests/fixtures/fail23.json", "tests/fixtures/fail24.json", "tests/fixtures/fail25.json", "tests/fixtures/fail27.json", "tests/fixtures/fail28.json", "tests/fixtures/fail3.json", "tests/fixtures/fail4.json", "tests/fixtures/fail5.json", "tests/fixtures/fail6.json", "tests/fixtures/fail7.json", "tests/fixtures/fail8.json", "tests/fixtures/fail9.json", "tests/fixtures/pass1.json", "tests/fixtures/pass15.json", "tests/fixtures/pass16.json", "tests/fixtures/pass17.json", "tests/fixtures/pass2.json", "tests/fixtures/pass26.json", "tests/fixtures/pass3.json", "tests/setup_variant.rb", "tests/test_json.rb", "tests/test_json_addition.rb", "tests/test_json_encoding.rb", "tests/test_json_fixtures.rb", "tests/test_json_generate.rb", "tests/test_json_generic_object.rb", "tests/test_json_string_matching.rb", "tests/test_json_unicode.rb", "tools/fuzz.rb", "tools/server.rb"] + s.extra_rdoc_files = ["README.md"] + s.files = ["./tests/test_helper.rb", ".gitignore", ".travis.yml", "CHANGES", "Gemfile", "README-json-jruby.markdown", "README.md", "Rakefile", "TODO", "VERSION", "data/example.json", "data/index.html", "data/prototype.js", "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", "ext/json/ext/generator/generator.c", "ext/json/ext/generator/generator.h", "ext/json/ext/parser/depend", "ext/json/ext/parser/extconf.rb", "ext/json/ext/parser/parser.c", "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", "install.rb", "java/src/json/ext/ByteListTranscoder.java", "java/src/json/ext/Generator.java", "java/src/json/ext/GeneratorMethods.java", "java/src/json/ext/GeneratorService.java", "java/src/json/ext/GeneratorState.java", "java/src/json/ext/OptionsReader.java", "java/src/json/ext/Parser.java", "java/src/json/ext/Parser.rl", "java/src/json/ext/ParserService.java", "java/src/json/ext/RuntimeInfo.java", "java/src/json/ext/StringDecoder.java", "java/src/json/ext/StringEncoder.java", "java/src/json/ext/Utils.java", "json-java.gemspec", "json.gemspec", "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", "lib/json/add/core.rb", "lib/json/add/date.rb", "lib/json/add/date_time.rb", "lib/json/add/exception.rb", "lib/json/add/ostruct.rb", "lib/json/add/range.rb", "lib/json/add/rational.rb", "lib/json/add/regexp.rb", "lib/json/add/struct.rb", "lib/json/add/symbol.rb", "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", "tests/fixtures/fail10.json", "tests/fixtures/fail11.json", "tests/fixtures/fail12.json", "tests/fixtures/fail13.json", "tests/fixtures/fail14.json", "tests/fixtures/fail18.json", "tests/fixtures/fail19.json", "tests/fixtures/fail2.json", "tests/fixtures/fail20.json", "tests/fixtures/fail21.json", "tests/fixtures/fail22.json", "tests/fixtures/fail23.json", "tests/fixtures/fail24.json", "tests/fixtures/fail25.json", "tests/fixtures/fail27.json", "tests/fixtures/fail28.json", "tests/fixtures/fail3.json", "tests/fixtures/fail4.json", "tests/fixtures/fail5.json", "tests/fixtures/fail6.json", "tests/fixtures/fail7.json", "tests/fixtures/fail8.json", "tests/fixtures/fail9.json", "tests/fixtures/obsolete_fail1.json", "tests/fixtures/pass1.json", "tests/fixtures/pass15.json", "tests/fixtures/pass16.json", "tests/fixtures/pass17.json", "tests/fixtures/pass2.json", "tests/fixtures/pass26.json", "tests/fixtures/pass3.json", "tests/json_addition_test.rb", "tests/json_common_interface_test.rb", "tests/json_encoding_test.rb", "tests/json_ext_parser_test.rb", "tests/json_fixtures_test.rb", "tests/json_generator_test.rb", "tests/json_generic_object_test.rb", "tests/json_parser_test.rb", "tests/json_string_matching_test.rb", "tests/test_helper.rb", "tools/fuzz.rb", "tools/server.rb"] s.homepage = "http://flori.github.com/json" s.licenses = ["Ruby"] - s.rdoc_options = ["--title", "JSON implemention for ruby", "--main", "README.rdoc"] - s.rubygems_version = "2.4.6" + s.rdoc_options = ["--title", "JSON implemention for ruby", "--main", "README.md"] + s.rubygems_version = "2.5.1" s.summary = "JSON Implementation for Ruby" - s.test_files = ["./tests/test_json.rb", "./tests/test_json_addition.rb", "./tests/test_json_encoding.rb", "./tests/test_json_fixtures.rb", "./tests/test_json_generate.rb", "./tests/test_json_generic_object.rb", "./tests/test_json_string_matching.rb", "./tests/test_json_unicode.rb"] + s.test_files = ["./tests/test_helper.rb"] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q<permutation>, [">= 0"]) - s.add_development_dependency(%q<sdoc>, ["~> 0.3.16"]) if RUBY_VERSION > "1.8.6" - s.add_development_dependency(%q<rake>, ["~> 0.9.2"]) + s.add_development_dependency(%q<rake>, [">= 0"]) + s.add_development_dependency(%q<test-unit>, ["~> 2.0"]) else - s.add_dependency(%q<permutation>, [">= 0"]) - s.add_dependency(%q<sdoc>, ["~> 0.3.16"]) if RUBY_VERSION > "1.8.6" - s.add_dependency(%q<rake>, ["~> 0.9.2"]) + s.add_dependency(%q<rake>, [">= 0"]) + s.add_dependency(%q<test-unit>, ["~> 2.0"]) end else - s.add_dependency(%q<permutation>, [">= 0"]) - s.add_dependency(%q<sdoc>, ["~> 0.3.16"]) if RUBY_VERSION > "1.8.6" - s.add_dependency(%q<rake>, ["~> 0.9.2"]) + s.add_dependency(%q<rake>, [">= 0"]) + s.add_dependency(%q<test-unit>, ["~> 2.0"]) end end diff --git a/lib/json/common.rb b/lib/json/common.rb index f44184e..1215d41 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -3,12 +3,12 @@ require 'json/generic_object' module JSON class << self - # If _object_ is string-like, parse the string and return the parsed result - # as a Ruby data structure. Otherwise generate a JSON text from the Ruby - # data structure object and return it. + # If _object_ is string-like, parse the string and return the parsed + # result as a Ruby data structure. Otherwise generate a JSON text from the + # Ruby data structure object and return it. # - # The _opts_ argument is passed through to generate/parse respectively. See - # generate and parse for their documentation. + # The _opts_ argument is passed through to generate/parse respectively. + # See generate and parse for their documentation. def [](object, opts = {}) if object.respond_to? :to_str JSON.parse(object.to_str, opts) @@ -138,8 +138,8 @@ module JSON # _opts_ can have the following # keys: # * *max_nesting*: The maximum depth of nesting allowed in the parsed data - # structures. Disable depth checking with :max_nesting => false. It defaults - # to 100. + # structures. Disable depth checking with :max_nesting => false. It + # defaults to 100. # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in # defiance of RFC 4627 to be parsed by the Parser. This option defaults # to false. @@ -161,9 +161,9 @@ module JSON # # _opts_ can have the following keys: # * *max_nesting*: The maximum depth of nesting allowed in the parsed data - # structures. Enable depth checking with :max_nesting => anInteger. The parse! - # methods defaults to not doing max depth checking: This can be dangerous - # if someone wants to fill up your stack. + # structures. Enable depth checking with :max_nesting => anInteger. The + # parse! methods defaults to not doing max depth checking: This can be + # dangerous if someone wants to fill up your stack. # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in # defiance of RFC 4627 to be parsed by the Parser. This option defaults # to true. diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 9056a5d..cc8b0fd 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -286,20 +286,14 @@ module JSON alias to_hash to_h - # Generates a valid JSON document from object +obj+ and returns the - # result. If no valid JSON document can be created this method raises a + # Generates a valid JSON document from object +obj+ and + # returns the result. If no valid JSON document can be + # created this method raises a # GeneratorError exception. def generate(obj) result = obj.to_json(self) JSON.valid_utf8?(result) or raise GeneratorError, "source sequence #{result.inspect} is illegal/malformed utf-8" - unless @quirks_mode - unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ || - result =~ /\A\s*\{/ && result =~ /\}\s*\Z/ - then - raise GeneratorError, "only generation of JSON objects or arrays allowed" - end - end result end @@ -363,7 +357,11 @@ module JSON result << state.space_before result << ':' result << state.space - result << value.to_json(state) + if value.respond_to?(:to_json) + result << value.to_json(state) + else + result << %{"#{String(value)}"} + end first = false } depth = state.depth -= 1 @@ -398,7 +396,11 @@ module JSON each { |value| result << delim unless first result << state.indent * depth if indent - result << value.to_json(state) + if value.respond_to?(:to_json) + result << value.to_json(state) + else + result << %{"#{String(value)}"} + end first = false } depth = state.depth -= 1 diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index a41d1ee..c5d1501 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -48,7 +48,7 @@ module JSON )+ )mx - UNPARSED = Object.new + UNPARSED = Object.new.freeze # Creates a new JSON::Pure::Parser instance for the string _source_. # @@ -61,8 +61,9 @@ module JSON # defiance of RFC 4627 to be parsed by the Parser. This option defaults # to false. # * *symbolize_names*: If set to true, returns symbols for the names - # (keys) in a JSON object. Otherwise strings are returned, which is also - # the default. + # (keys) in a JSON object. Otherwise strings are returned, which is + # also the default. It's not possible to use this option in + # conjunction with the *create_additions* option. # * *create_additions*: If set to true, the Parser creates # additions when if a matching class and create_id was found. This # option defaults to false. @@ -90,6 +91,9 @@ module JSON else @create_additions = false end + @symbolize_names && @create_additions and raise ArgumentError, + 'options :symbolize_names and :create_additions cannot be used '\ + 'in conjunction' @create_id = @create_additions ? JSON.create_id : nil @object_class = opts[:object_class] || Hash @array_class = opts[:array_class] || Array @@ -107,39 +111,21 @@ module JSON @current_nesting = 0 end - # Parses the current JSON string _source_ and returns the complete data - # structure as a result. + # Parses the current JSON string _source_ and returns the + # complete data structure as a result. def parse reset obj = nil - if @quirks_mode - while !eos? && skip(IGNORE) - end - if eos? - raise ParserError, "source did not contain any JSON!" - else - obj = parse_value - obj == UNPARSED and raise ParserError, "source did not contain any JSON!" - end + while !eos? && skip(IGNORE) do end + if eos? + raise ParserError, "source is not valid JSON!" else - until eos? - case - when scan(OBJECT_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in JSON!" - @current_nesting = 1 - obj = parse_object - when scan(ARRAY_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in JSON!" - @current_nesting = 1 - obj = parse_array - when skip(IGNORE) - ; - else - raise ParserError, "source '#{peek(20)}' not in JSON!" - end - end - obj or raise ParserError, "source did not contain any JSON!" + obj = parse_value + UNPARSED.equal?(obj) and raise ParserError, + "source is not valid JSON!" end + while !eos? && skip(IGNORE) do end + eos? or raise ParserError, "source is not valid JSON!" obj end @@ -149,43 +135,12 @@ module JSON if source.respond_to?(:to_str) source = source.to_str else - raise TypeError, "#{source.inspect} is not like a string" + raise TypeError, + "#{source.inspect} is not like a string" end if defined?(::Encoding) - if source.encoding == ::Encoding::ASCII_8BIT - b = source[0, 4].bytes.to_a - source = - case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[1] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) - else - source.dup - end - else - source = source.encode(::Encoding::UTF_8) - end + source = source.encode(::Encoding::UTF_8) source.force_encoding(::Encoding::ASCII_8BIT) - else - b = source - source = - case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-32be', b) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-16be', b) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-32le', b) - when b.size >= 4 && b[1] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-16le', b) - else - b - end end source end @@ -254,7 +209,7 @@ module JSON false when scan(NULL) nil - when (string = parse_string) != UNPARSED + when !UNPARSED.equal?(string = parse_string) string when scan(ARRAY_OPEN) @current_nesting += 1 @@ -284,7 +239,7 @@ module JSON delim = false until eos? case - when (value = parse_value) != UNPARSED + when !UNPARSED.equal?(value = parse_value) delim = false result << value skip(IGNORE) @@ -316,13 +271,13 @@ module JSON delim = false until eos? case - when (string = parse_string) != UNPARSED + when !UNPARSED.equal?(string = parse_string) skip(IGNORE) unless scan(PAIR_DELIMITER) raise ParserError, "expected ':' in object at '#{peek(20)}'!" end skip(IGNORE) - unless (value = parse_value).equal? UNPARSED + unless UNPARSED.equal?(value = parse_value) result[@symbolize_names ? string.to_sym : string] = value delim = false skip(IGNORE) diff --git a/lib/json/version.rb b/lib/json/version.rb index 5a4013d..b91ccd7 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,6 +1,6 @@ module JSON # JSON version - VERSION = '1.8.3' + VERSION = '2.0.0' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: diff --git a/tests/fixtures/fail1.json b/tests/fixtures/fail1.json deleted file mode 100644 index 6216b86..0000000 --- a/tests/fixtures/fail1.json +++ /dev/null @@ -1 +0,0 @@ -"A JSON payload should be an object or array, not a string."
\ No newline at end of file diff --git a/tests/fixtures/obsolete_fail1.json b/tests/fixtures/obsolete_fail1.json new file mode 100644 index 0000000..98b77de --- /dev/null +++ b/tests/fixtures/obsolete_fail1.json @@ -0,0 +1 @@ +"A JSON payload should be an object or array, not a string." diff --git a/tests/test_json_addition.rb b/tests/json_addition_test.rb index a30f06a..128f34a 100755..100644 --- a/tests/test_json_addition.rb +++ b/tests/json_addition_test.rb @@ -1,8 +1,4 @@ -#!/usr/bin/env ruby -# -*- coding:utf-8 -*- - -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') +require 'test_helper' require 'json/add/core' require 'json/add/complex' require 'json/add/rational' @@ -10,7 +6,7 @@ require 'json/add/bigdecimal' require 'json/add/ostruct' require 'date' -class TestJSONAddition < Test::Unit::TestCase +class JSONAdditionTest < Test::Unit::TestCase include JSON class A @@ -64,7 +60,7 @@ class TestJSONAddition < Test::Unit::TestCase def to_json(*args) { - 'json_class' => 'TestJSONAddition::Nix', + 'json_class' => 'JSONAdditionTest::Nix', }.to_json(*args) end end @@ -73,7 +69,7 @@ class TestJSONAddition < Test::Unit::TestCase a = A.new(666) assert A.json_creatable? json = generate(a) - a_again = JSON.parse(json, :create_additions => true) + a_again = parse(json, :create_additions => true) assert_kind_of a.class, a_again assert_equal a, a_again end @@ -82,7 +78,7 @@ class TestJSONAddition < Test::Unit::TestCase a = A.new(666) assert A.json_creatable? json = generate(a) - a_hash = JSON.parse(json) + a_hash = parse(json) assert_kind_of Hash, a_hash end @@ -90,13 +86,13 @@ class TestJSONAddition < Test::Unit::TestCase a = A.new(666) assert A.json_creatable? json = generate(a) - a_again = JSON.parse(json, :create_additions => true) + a_again = parse(json, :create_additions => true) assert_kind_of a.class, a_again assert_equal a, a_again - a_hash = JSON.parse(json, :create_additions => false) + a_hash = parse(json, :create_additions => false) assert_kind_of Hash, a_hash assert_equal( - {"args"=>[666], "json_class"=>"TestJSONAddition::A"}.sort_by { |k,| k }, + {"args"=>[666], "json_class"=>"JSONAdditionTest::A"}.sort_by { |k,| k }, a_hash.sort_by { |k,| k } ) end @@ -105,14 +101,14 @@ class TestJSONAddition < Test::Unit::TestCase b = B.new assert !B.json_creatable? json = generate(b) - assert_equal({ "json_class"=>"TestJSONAddition::B" }, JSON.parse(json)) + assert_equal({ "json_class"=>"JSONAdditionTest::B" }, parse(json)) end def test_extended_json_fail2 c = C.new assert !C.json_creatable? json = generate(c) - assert_raises(ArgumentError, NameError) { JSON.parse(json, :create_additions => true) } + assert_raises(ArgumentError, NameError) { parse(json, :create_additions => true) } end def test_raw_strings @@ -130,7 +126,7 @@ class TestJSONAddition < Test::Unit::TestCase assert_match(/\A\{.*\}\z/, json) assert_match(/"json_class":"String"/, json) assert_match(/"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\]/, json) - raw_again = JSON.parse(json, :create_additions => true) + raw_again = parse(json, :create_additions => true) assert_equal raw, raw_again end @@ -167,19 +163,19 @@ class TestJSONAddition < Test::Unit::TestCase def test_utc_datetime now = Time.now - d = DateTime.parse(now.to_s, :create_additions => true) # usual case - assert_equal d, JSON.parse(d.to_json, :create_additions => true) - d = DateTime.parse(now.utc.to_s) # of = 0 - assert_equal d, JSON.parse(d.to_json, :create_additions => true) + d = DateTime.parse(now.to_s, :create_additions => true) # usual case + assert_equal d, parse(d.to_json, :create_additions => true) + d = DateTime.parse(now.utc.to_s) # of = 0 + assert_equal d, parse(d.to_json, :create_additions => true) d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(1,24)) - assert_equal d, JSON.parse(d.to_json, :create_additions => true) + assert_equal d, parse(d.to_json, :create_additions => true) d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(12,24)) - assert_equal d, JSON.parse(d.to_json, :create_additions => true) + assert_equal d, parse(d.to_json, :create_additions => true) end def test_rational_complex - assert_equal Rational(2, 9), JSON.parse(JSON(Rational(2, 9)), :create_additions => true) - assert_equal Complex(2, 9), JSON.parse(JSON(Complex(2, 9)), :create_additions => true) + assert_equal Rational(2, 9), parse(JSON(Rational(2, 9)), :create_additions => true) + assert_equal Complex(2, 9), parse(JSON(Complex(2, 9)), :create_additions => true) end def test_bigdecimal @@ -191,6 +187,6 @@ class TestJSONAddition < Test::Unit::TestCase o = OpenStruct.new # XXX this won't work; o.foo = { :bar => true } o.foo = { 'bar' => true } - assert_equal o, JSON.parse(JSON(o), :create_additions => true) + assert_equal o, parse(JSON(o), :create_additions => true) end end diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb new file mode 100644 index 0000000..6485328 --- /dev/null +++ b/tests/json_common_interface_test.rb @@ -0,0 +1,98 @@ +require 'test_helper' +require 'stringio' +require 'tempfile' + +class JSONCommonInterfaceTest < Test::Unit::TestCase + include JSON + + def setup + @hash = { + 'a' => 2, + 'b' => 3.141, + 'c' => 'c', + 'd' => [ 1, "b", 3.14 ], + 'e' => { 'foo' => 'bar' }, + 'g' => "\"\0\037", + 'h' => 1000.0, + 'i' => 0.001 + } + @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\ + '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}' + end + + def test_index + end + + def test_parser + end + + def test_generator + end + + def test_state + end + + def test_create_id + end + + def test_parse + end + + def test_parse_bang + end + + def test_generate + end + + def test_fast_generate + end + + def test_pretty_generate + end + + def test_load + assert_equal @hash, JSON.load(@json) + tempfile = Tempfile.open('@json') + tempfile.write @json + tempfile.rewind + assert_equal @hash, JSON.load(tempfile) + stringio = StringIO.new(@json) + stringio.rewind + assert_equal @hash, JSON.load(stringio) + assert_equal nil, JSON.load(nil) + assert_equal nil, JSON.load('') + ensure + tempfile.close! + end + + def test_load_with_options + json = '{ "foo": NaN }' + assert JSON.load(json, nil, :allow_nan => true)['foo'].nan? + end + + def test_dump + too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' + assert_equal too_deep, dump(eval(too_deep)) + assert_kind_of String, Marshal.dump(eval(too_deep)) + assert_raises(ArgumentError) { dump(eval(too_deep), 100) } + assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 100) } + assert_equal too_deep, dump(eval(too_deep), 101) + assert_kind_of String, Marshal.dump(eval(too_deep), 101) + output = StringIO.new + dump(eval(too_deep), output) + assert_equal too_deep, output.string + output = StringIO.new + dump(eval(too_deep), output, 101) + assert_equal too_deep, output.string + end + + def test_dump_should_modify_defaults + max_nesting = JSON.dump_default_options[:max_nesting] + dump([], StringIO.new, 10) + assert_equal max_nesting, JSON.dump_default_options[:max_nesting] + end + + + def test_JSON + end +end diff --git a/tests/json_encoding_test.rb b/tests/json_encoding_test.rb new file mode 100644 index 0000000..86335a1 --- /dev/null +++ b/tests/json_encoding_test.rb @@ -0,0 +1,109 @@ +require 'test_helper' + +class JSONEncodingTest < Test::Unit::TestCase + include JSON + + def setup + @utf_8 = '"© ≠ €!"' + @parsed = "© ≠ €!" + @generated = '"\u00a9 \u2260 \u20ac!"' + if String.method_defined?(:encode) + @utf_16_data = @parsed.encode('utf-16be', 'utf-8') + @utf_16be = @utf_8.encode('utf-16be', 'utf-8') + @utf_16le = @utf_8.encode('utf-16le', 'utf-8') + @utf_32be = @utf_8.encode('utf-32be', 'utf-8') + @utf_32le = @utf_8.encode('utf-32le', 'utf-8') + else + require 'iconv' + @utf_16_data, = Iconv.iconv('utf-16be', 'utf-8', @parsed) + @utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8) + @utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8) + @utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8) + @utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8) + end + end + + def test_parse + assert_equal @parsed, JSON.parse(@utf_8) + assert_equal @parsed, JSON.parse(@utf_16be) + assert_equal @parsed, JSON.parse(@utf_16le) + assert_equal @parsed, JSON.parse(@utf_32be) + assert_equal @parsed, JSON.parse(@utf_32le) + end + + def test_generate + assert_equal @generated, JSON.generate(@parsed, :ascii_only => true) + if defined?(::Encoding) + assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true) + else + # XXX checking of correct utf8 data is not as strict (yet?) without + # :ascii_only + assert_raises(JSON::GeneratorError) do + JSON.generate(@utf_16_data, :ascii_only => true) + end + end + end + + def test_unicode + assert_equal '""', ''.to_json + assert_equal '"\\b"', "\b".to_json + assert_equal '"\u0001"', 0x1.chr.to_json + assert_equal '"\u001f"', 0x1f.chr.to_json + assert_equal '" "', ' '.to_json + assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json + utf8 = [ "© ≠ €! \01" ] + json = '["© ≠ €! \u0001"]' + assert_equal json, utf8.to_json(:ascii_only => false) + assert_equal utf8, parse(json) + json = '["\u00a9 \u2260 \u20ac! \u0001"]' + assert_equal json, utf8.to_json(:ascii_only => true) + assert_equal utf8, parse(json) + utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"] + json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]" + assert_equal utf8, parse(json) + assert_equal json, utf8.to_json(:ascii_only => false) + utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"] + assert_equal utf8, parse(json) + json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]" + assert_equal json, utf8.to_json(:ascii_only => true) + assert_equal utf8, parse(json) + utf8 = ['საქართველო'] + json = '["საქართველო"]' + assert_equal json, utf8.to_json(:ascii_only => false) + json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]" + assert_equal json, utf8.to_json(:ascii_only => true) + assert_equal utf8, parse(json) + assert_equal '["Ã"]', generate(["Ã"], :ascii_only => false) + assert_equal '["\\u00c3"]', generate(["Ã"], :ascii_only => true) + assert_equal ["€"], parse('["\u20ac"]') + utf8 = ["\xf0\xa0\x80\x81"] + json = "[\"\xf0\xa0\x80\x81\"]" + assert_equal json, generate(utf8, :ascii_only => false) + assert_equal utf8, parse(json) + json = '["\ud840\udc01"]' + assert_equal json, generate(utf8, :ascii_only => true) + assert_equal utf8, parse(json) + end + + def test_chars + (0..0x7f).each do |i| + json = '["\u%04x"]' % i + if RUBY_VERSION >= "1.9." + i = i.chr + end + assert_equal i, parse(json).first[0] + if i == ?\b + generated = generate(["" << i]) + assert '["\b"]' == generated || '["\10"]' == generated + elsif [?\n, ?\r, ?\t, ?\f].include?(i) + assert_equal '[' << ('' << i).dump << ']', generate(["" << i]) + elsif i.chr < 0x20.chr + assert_equal json, generate(["" << i]) + end + end + assert_raise(JSON::GeneratorError) do + generate(["\x80"], :ascii_only => true) + end + assert_equal "\302\200", parse('["\u0080"]').first + end +end diff --git a/tests/json_ext_parser_test.rb b/tests/json_ext_parser_test.rb new file mode 100644 index 0000000..da3bdc1 --- /dev/null +++ b/tests/json_ext_parser_test.rb @@ -0,0 +1,17 @@ +require 'test_helper' +require 'stringio' +require 'tempfile' +require 'ostruct' + +class JSONExtParserTest < Test::Unit::TestCase + if defined?(JSON::Ext::Parser) + def test_allocate + parser = JSON::Ext::Parser.new("{}") + assert_raise(TypeError, '[ruby-core:35079]') do + parser.__send__(:initialize, "{}") + end + parser = JSON::Ext::Parser.allocate + assert_raise(TypeError, '[ruby-core:35079]') { parser.source } + end + end +end diff --git a/tests/test_json_fixtures.rb b/tests/json_fixtures_test.rb index 584dffd..6681b8d 100755..100644 --- a/tests/test_json_fixtures.rb +++ b/tests/json_fixtures_test.rb @@ -1,12 +1,8 @@ -#!/usr/bin/env ruby -# encoding: utf-8 +require 'test_helper' -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') - -class TestJSONFixtures < Test::Unit::TestCase +class JSONFixturesTest < Test::Unit::TestCase def setup - fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.json') + fixtures = File.join(File.dirname(__FILE__), 'fixtures/{fail,pass}.json') passed, failed = Dir[fixtures].partition { |f| f['pass'] } @passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort @failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort diff --git a/tests/test_json_generate.rb b/tests/json_generator_test.rb index 8db0b78..2bb7e15 100755..100644 --- a/tests/test_json_generate.rb +++ b/tests/json_generator_test.rb @@ -1,10 +1,10 @@ #!/usr/bin/env ruby # encoding: utf-8 +# frozen_string_literal: false -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') +require 'test_helper' -class TestJSONGenerate < Test::Unit::TestCase +class JSONGeneratorTest < Test::Unit::TestCase include JSON def setup @@ -42,23 +42,23 @@ EOT def test_generate json = generate(@hash) - assert_equal(JSON.parse(@json2), JSON.parse(json)) + assert_equal(parse(@json2), parse(json)) json = JSON[@hash] - assert_equal(JSON.parse(@json2), JSON.parse(json)) + assert_equal(parse(@json2), parse(json)) parsed_json = parse(json) assert_equal(@hash, parsed_json) json = generate({1=>2}) assert_equal('{"1":2}', json) parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) - assert_raise(GeneratorError) { generate(666) } - assert_equal '666', generate(666, :quirks_mode => true) + assert_equal '666', generate(666) end def test_generate_pretty json = pretty_generate(@hash) - # hashes aren't (insertion) ordered on every ruby implementation assert_equal(@json3, json) - assert_equal(JSON.parse(@json3), JSON.parse(json)) + # hashes aren't (insertion) ordered on every ruby implementation + # assert_equal(@json3, json) + assert_equal(parse(@json3), parse(json)) parsed_json = parse(json) assert_equal(@hash, parsed_json) json = pretty_generate({1=>2}) @@ -69,8 +69,7 @@ EOT EOT parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) - assert_raise(GeneratorError) { pretty_generate(666) } - assert_equal '666', pretty_generate(666, :quirks_mode => true) + assert_equal '666', pretty_generate(666) end def test_generate_custom @@ -88,30 +87,26 @@ EOT def test_fast_generate json = fast_generate(@hash) - assert_equal(JSON.parse(@json2), JSON.parse(json)) + assert_equal(parse(@json2), parse(json)) parsed_json = parse(json) assert_equal(@hash, parsed_json) json = fast_generate({1=>2}) assert_equal('{"1":2}', json) parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) - assert_raise(GeneratorError) { fast_generate(666) } - assert_equal '666', fast_generate(666, :quirks_mode => true) + assert_equal '666', fast_generate(666) end def test_own_state state = State.new json = generate(@hash, state) - assert_equal(JSON.parse(@json2), JSON.parse(json)) + assert_equal(parse(@json2), parse(json)) parsed_json = parse(json) assert_equal(@hash, parsed_json) json = generate({1=>2}, state) assert_equal('{"1":2}', json) parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) - assert_raise(GeneratorError) { generate(666, state) } - state.quirks_mode = true - assert state.quirks_mode? assert_equal '666', generate(666, state) end @@ -205,7 +200,7 @@ EOT def test_depth ary = []; ary << ary assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth - assert_raises(JSON::NestingError) { JSON.generate(ary) } + assert_raises(JSON::NestingError) { generate(ary) } assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth assert_raises(JSON::NestingError) { JSON.pretty_generate(ary) } @@ -331,7 +326,55 @@ EOT def test_json_generate assert_raise JSON::GeneratorError do - assert_equal true, JSON.generate(["\xea"]) + assert_equal true, generate(["\xea"]) end end + + def test_nesting + too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' + too_deep_ary = eval too_deep + assert_raises(JSON::NestingError) { generate too_deep_ary } + assert_raises(JSON::NestingError) { generate too_deep_ary, :max_nesting => 100 } + ok = generate too_deep_ary, :max_nesting => 101 + assert_equal too_deep, ok + ok = generate too_deep_ary, :max_nesting => nil + assert_equal too_deep, ok + ok = generate too_deep_ary, :max_nesting => false + assert_equal too_deep, ok + ok = generate too_deep_ary, :max_nesting => 0 + assert_equal too_deep, ok + end + + def test_backslash + data = [ '\\.(?i:gif|jpe?g|png)$' ] + json = '["\\\\.(?i:gif|jpe?g|png)$"]' + assert_equal json, generate(data) + # + data = [ '\\"' ] + json = '["\\\\\""]' + assert_equal json, generate(data) + # + data = [ '/' ] + json = '["/"]' + assert_equal json, generate(data) + # + data = ['"'] + json = '["\""]' + assert_equal json, generate(data) + # + data = ["'"] + json = '["\\\'"]' + assert_equal '["\'"]', generate(data) + end + + def test_string_subclass + s = Class.new(String) do + def to_s; self; end + undef to_json + end + assert_nothing_raised(SystemStackError) do + assert_equal '["foo"]', JSON.generate([s.new('foo')]) + end + + end end diff --git a/tests/test_json_generic_object.rb b/tests/json_generic_object_test.rb index c43c776..171fdb8 100644 --- a/tests/test_json_generic_object.rb +++ b/tests/json_generic_object_test.rb @@ -1,9 +1,6 @@ -#!/usr/bin/env ruby -# encoding: utf-8 +require 'test_helper' -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') -class TestJSONGenericObject < Test::Unit::TestCase +class JSONGenericObjectTest < Test::Unit::TestCase include JSON def setup @@ -26,11 +23,20 @@ class TestJSONGenericObject < Test::Unit::TestCase end def test_parse_json - assert_kind_of Hash, JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true) + assert_kind_of Hash, + JSON( + '{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', + :create_additions => true + ) switch_json_creatable do - assert_equal @go, l = JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true) + assert_equal @go, l = + JSON( + '{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', + :create_additions => true + ) assert_equal 1, l.a - assert_equal @go, l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject) + assert_equal @go, + l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject) assert_equal 1, l.a assert_equal GenericObject[:a => GenericObject[:b => 2]], l = JSON('{ "a": { "b": 2 } }', :object_class => GenericObject) diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb new file mode 100644 index 0000000..b21e7ec --- /dev/null +++ b/tests/json_parser_test.rb @@ -0,0 +1,436 @@ +require 'test_helper' +require 'stringio' +require 'tempfile' +require 'ostruct' + +class JSONParserTest < Test::Unit::TestCase + include JSON + + def test_construction + parser = JSON::Parser.new('test') + assert_equal 'test', parser.source + end + + def test_argument_encoding + source = "{}".encode("UTF-16") + JSON::Parser.new(source) + assert_equal Encoding::UTF_16, source.encoding + end if defined?(Encoding::UTF_16) + + def test_parsing + parser = JSON::Parser.new('"test"') + assert_equal 'test', parser.parse + end + + def test_parser_reset + parser = Parser.new('{"a":"b"}') + assert_equal({ 'a' => 'b' }, parser.parse) + assert_equal({ 'a' => 'b' }, parser.parse) + end + + def test_parse_simple_arrays + assert_equal([], parse('[]')) + assert_equal([], parse(' [ ] ')) + assert_equal([ nil ], parse('[null]')) + assert_equal([ false ], parse('[false]')) + assert_equal([ true ], parse('[true]')) + assert_equal([ -23 ], parse('[-23]')) + assert_equal([ 23 ], parse('[23]')) + assert_equal_float([ 0.23 ], parse('[0.23]')) + assert_equal_float([ 0.0 ], parse('[0e0]')) + assert_equal([""], parse('[""]')) + assert_equal(["foobar"], parse('["foobar"]')) + assert_equal([{}], parse('[{}]')) + end + + def test_parse_simple_objects + assert_equal({}, parse('{}')) + assert_equal({}, parse(' { } ')) + assert_equal({ "a" => nil }, parse('{ "a" : null}')) + assert_equal({ "a" => nil }, parse('{"a":null}')) + assert_equal({ "a" => false }, parse('{ "a" : false } ')) + assert_equal({ "a" => false }, parse('{"a":false}')) + assert_raises(JSON::ParserError) { parse('{false}') } + assert_equal({ "a" => true }, parse('{"a":true}')) + assert_equal({ "a" => true }, parse(' { "a" : true } ')) + assert_equal({ "a" => -23 }, parse(' { "a" : -23 } ')) + assert_equal({ "a" => -23 }, parse(' { "a" : -23 } ')) + assert_equal({ "a" => 23 }, parse('{"a":23 } ')) + assert_equal({ "a" => 23 }, parse(' { "a" : 23 } ')) + assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) + assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) + end + + def test_parse_numbers + assert_raises(JSON::ParserError) { parse('+23.2') } + assert_raises(JSON::ParserError) { parse('+23') } + assert_raises(JSON::ParserError) { parse('.23') } + assert_raises(JSON::ParserError) { parse('023') } + assert_equal 23, parse('23') + assert_equal -23, parse('-23') + assert_equal_float 3.141, parse('3.141') + assert_equal_float -3.141, parse('-3.141') + assert_equal_float 3.141, parse('3141e-3') + assert_equal_float 3.141, parse('3141.1e-3') + assert_equal_float 3.141, parse('3141E-3') + assert_equal_float 3.141, parse('3141.0E-3') + assert_equal_float -3.141, parse('-3141.0e-3') + assert_equal_float -3.141, parse('-3141e-3') + assert_raises(ParserError) { parse('NaN') } + assert parse('NaN', :allow_nan => true).nan? + assert_raises(ParserError) { parse('Infinity') } + assert_equal 1.0/0, parse('Infinity', :allow_nan => true) + assert_raises(ParserError) { parse('-Infinity') } + assert_equal -1.0/0, parse('-Infinity', :allow_nan => true) + end + + if Array.method_defined?(:permutation) + def test_parse_more_complex_arrays + a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] + a.permutation.each do |perm| + json = pretty_generate(perm) + assert_equal perm, parse(json) + end + end + + def test_parse_complex_objects + a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] + a.permutation.each do |perm| + s = "a" + orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h } + json = pretty_generate(orig_obj) + assert_equal orig_obj, parse(json) + end + end + end + + def test_parse_arrays + assert_equal([1,2,3], parse('[1,2,3]')) + assert_equal([1.2,2,3], parse('[1.2,2,3]')) + assert_equal([[],[[],[]]], parse('[[],[[],[]]]')) + assert_equal([], parse('[]')) + assert_equal([], parse(' [ ] ')) + assert_equal([1], parse('[1]')) + assert_equal([1], parse(' [ 1 ] ')) + ary = [[1], ["foo"], [3.14], [4711.0], [2.718], [nil], + [[1, -2, 3]], [false], [true]] + assert_equal(ary, + parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]],[false],[true]]')) + assert_equal(ary, parse(%Q{ [ [1] , ["foo"] , [3.14] \t , [47.11e+2]\s + , [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] })) + end + + def test_parse_json_primitive_values + assert_raise(JSON::ParserError) { parse('') } + assert_raise(TypeError) { parse(nil) } + assert_raise(JSON::ParserError) { parse(' /* foo */ ') } + assert_equal nil, parse('null') + assert_equal false, parse('false') + assert_equal true, parse('true') + assert_equal 23, parse('23') + assert_equal 1, parse('1') + assert_equal_float 3.141, parse('3.141'), 1E-3 + assert_equal 2 ** 64, parse('18446744073709551616') + assert_equal 'foo', parse('"foo"') + assert parse('NaN', :allow_nan => true).nan? + assert parse('Infinity', :allow_nan => true).infinite? + assert parse('-Infinity', :allow_nan => true).infinite? + assert_raise(JSON::ParserError) { parse('[ 1, ]', :quirks_mode => true) } + end + + def test_parse_some_strings + assert_equal([""], parse('[""]')) + assert_equal(["\\"], parse('["\\\\"]')) + assert_equal(['"'], parse('["\""]')) + assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]')) + assert_equal( + ["\"\b\n\r\t\0\037"], + parse('["\"\b\n\r\t\u0000\u001f"]') + ) + end + + def test_parse_big_integers + json1 = JSON(orig = (1 << 31) - 1) + assert_equal orig, parse(json1) + json2 = JSON(orig = 1 << 31) + assert_equal orig, parse(json2) + json3 = JSON(orig = (1 << 62) - 1) + assert_equal orig, parse(json3) + json4 = JSON(orig = 1 << 62) + assert_equal orig, parse(json4) + json5 = JSON(orig = 1 << 64) + assert_equal orig, parse(json5) + end + + def test_some_wrong_inputs + assert_raises(ParserError) { parse('[] bla') } + assert_raises(ParserError) { parse('[] 1') } + assert_raises(ParserError) { parse('[] []') } + assert_raises(ParserError) { parse('[] {}') } + assert_raises(ParserError) { parse('{} []') } + assert_raises(ParserError) { parse('{} {}') } + assert_raises(ParserError) { parse('[NULL]') } + assert_raises(ParserError) { parse('[FALSE]') } + assert_raises(ParserError) { parse('[TRUE]') } + assert_raises(ParserError) { parse('[07] ') } + assert_raises(ParserError) { parse('[0a]') } + assert_raises(ParserError) { parse('[1.]') } + assert_raises(ParserError) { parse(' ') } + end + + def test_symbolize_names + assert_equal({ "foo" => "bar", "baz" => "quux" }, + parse('{"foo":"bar", "baz":"quux"}')) + assert_equal({ :foo => "bar", :baz => "quux" }, + parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true)) + assert_raise(ArgumentError) do + parse('{}', :symbolize_names => true, :create_additions => true) + end + end + + def test_parse_comments + json = <<EOT +{ + "key1":"value1", // eol comment + "key2":"value2" /* multi line + * comment */, + "key3":"value3" /* multi line + // nested eol comment + * comment */ +} +EOT + assert_equal( + { "key1" => "value1", "key2" => "value2", "key3" => "value3" }, + parse(json)) + json = <<EOT +{ + "key1":"value1" /* multi line + // nested eol comment + /* illegal nested multi line comment */ + * comment */ +} +EOT + assert_raises(ParserError) { parse(json) } + json = <<EOT +{ + "key1":"value1" /* multi line + // nested eol comment + closed multi comment */ + and again, throw an Error */ +} +EOT + assert_raises(ParserError) { parse(json) } + json = <<EOT +{ + "key1":"value1" /*/*/ +} +EOT + assert_equal({ "key1" => "value1" }, parse(json)) + end + + def test_nesting + too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' + too_deep_ary = eval too_deep + assert_raises(JSON::NestingError) { parse too_deep } + assert_raises(JSON::NestingError) { parse too_deep, :max_nesting => 100 } + ok = parse too_deep, :max_nesting => 101 + assert_equal too_deep_ary, ok + ok = parse too_deep, :max_nesting => nil + assert_equal too_deep_ary, ok + ok = parse too_deep, :max_nesting => false + assert_equal too_deep_ary, ok + ok = parse too_deep, :max_nesting => 0 + assert_equal too_deep_ary, ok + end + + def test_backslash + data = [ '\\.(?i:gif|jpe?g|png)$' ] + json = '["\\\\.(?i:gif|jpe?g|png)$"]' + assert_equal data, parse(json) + # + data = [ '\\"' ] + json = '["\\\\\""]' + assert_equal data, parse(json) + # + json = '["/"]' + data = [ '/' ] + assert_equal data, parse(json) + # + json = '["\""]' + data = ['"'] + assert_equal data, parse(json) + # + json = '["\\\'"]' + data = ["'"] + assert_equal data, parse(json) + end + + + class SubArray < Array + def <<(v) + @shifted = true + super + end + + def shifted? + @shifted + end + end + + class SubArray2 < Array + def to_json(*a) + { + JSON.create_id => self.class.name, + 'ary' => to_a, + }.to_json(*a) + end + + def self.json_create(o) + o.delete JSON.create_id + o['ary'] + end + end + + class SubArrayWrapper + def initialize + @data = [] + end + + attr_reader :data + + def [](index) + @data[index] + end + + def <<(value) + @data << value + @shifted = true + end + + def shifted? + @shifted + end + end + + def test_parse_array_custom_array_derived_class + res = parse('[1,2]', :array_class => SubArray) + assert_equal([1,2], res) + assert_equal(SubArray, res.class) + assert res.shifted? + end + + def test_parse_array_custom_non_array_derived_class + res = parse('[1,2]', :array_class => SubArrayWrapper) + assert_equal([1,2], res.data) + assert_equal(SubArrayWrapper, res.class) + assert res.shifted? + end + + def test_parse_object + assert_equal({}, parse('{}')) + assert_equal({}, parse(' { } ')) + assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}')) + assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } ')) + end + + class SubHash < Hash + def []=(k, v) + @item_set = true + super + end + + def item_set? + @item_set + end + end + + class SubHash2 < Hash + def to_json(*a) + { + JSON.create_id => self.class.name, + }.merge(self).to_json(*a) + end + + def self.json_create(o) + o.delete JSON.create_id + self[o] + end + end + + class SubOpenStruct < OpenStruct + def [](k) + __send__(k) + end + + def []=(k, v) + @item_set = true + __send__("#{k}=", v) + end + + def item_set? + @item_set + end + end + + def test_parse_object_custom_hash_derived_class + res = parse('{"foo":"bar"}', :object_class => SubHash) + assert_equal({"foo" => "bar"}, res) + assert_equal(SubHash, res.class) + assert res.item_set? + end + + def test_parse_object_custom_non_hash_derived_class + res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) + assert_equal "bar", res.foo + assert_equal(SubOpenStruct, res.class) + assert res.item_set? + end + + def test_parse_generic_object + res = parse( + '{"foo":"bar", "baz":{}}', + :object_class => JSON::GenericObject + ) + assert_equal(JSON::GenericObject, res.class) + assert_equal "bar", res.foo + assert_equal "bar", res["foo"] + assert_equal "bar", res[:foo] + assert_equal "bar", res.to_hash[:foo] + assert_equal(JSON::GenericObject, res.baz.class) + end + + def test_generate_core_subclasses_with_new_to_json + obj = SubHash2["foo" => SubHash2["bar" => true]] + obj_json = JSON(obj) + obj_again = parse(obj_json, :create_additions => true) + assert_kind_of SubHash2, obj_again + assert_kind_of SubHash2, obj_again['foo'] + assert obj_again['foo']['bar'] + assert_equal obj, obj_again + assert_equal ["foo"], + JSON(JSON(SubArray2["foo"]), :create_additions => true) + end + + def test_generate_core_subclasses_with_default_to_json + assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"]) + assert_equal '["foo"]', JSON(SubArray["foo"]) + end + + def test_generate_of_core_subclasses + obj = SubHash["foo" => SubHash["bar" => true]] + obj_json = JSON(obj) + obj_again = JSON(obj_json) + assert_kind_of Hash, obj_again + assert_kind_of Hash, obj_again['foo'] + assert obj_again['foo']['bar'] + assert_equal obj, obj_again + end + + private + + def assert_equal_float(expected, actual, delta = 1e-2) + Array === expected and expected = expected.first + Array === actual and actual = actual.first + assert_in_delta(expected, actual, delta) + end +end diff --git a/tests/json_string_matching_test.rb b/tests/json_string_matching_test.rb new file mode 100644 index 0000000..7fec841 --- /dev/null +++ b/tests/json_string_matching_test.rb @@ -0,0 +1,37 @@ +require 'test_helper' +require 'time' + +class JSONStringMatchingTest < Test::Unit::TestCase + include JSON + + class TestTime < ::Time + def self.json_create(string) + Time.parse(string) + end + + def to_json(*) + %{"#{strftime('%FT%T%z')}"} + end + + def ==(other) + to_i == other.to_i + end + end + + def test_match_date + t = TestTime.new + t_json = [ t ].to_json + time_regexp = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ + assert_equal [ t ], + parse( + t_json, + :create_additions => true, + :match_string => { time_regexp => TestTime } + ) + assert_equal [ t.strftime('%FT%T%z') ], + parse( + t_json, + :match_string => { time_regexp => TestTime } + ) + end +end diff --git a/tests/setup_variant.rb b/tests/test_helper.rb index 2dab184..752f5f5 100644 --- a/tests/setup_variant.rb +++ b/tests/test_helper.rb @@ -1,3 +1,5 @@ +gem 'json', File.read('VERSION').chomp + case ENV['JSON'] when 'pure' $:.unshift 'lib' @@ -9,3 +11,9 @@ else $:.unshift 'ext', 'lib' require 'json' end + +require 'test/unit' +begin + require 'byebug' +rescue LoadError +end diff --git a/tests/test_json.rb b/tests/test_json.rb deleted file mode 100755 index 7957773..0000000 --- a/tests/test_json.rb +++ /dev/null @@ -1,553 +0,0 @@ -#!/usr/bin/env ruby -# encoding: utf-8 - -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') -require 'stringio' -require 'tempfile' -require 'ostruct' - -unless Array.method_defined?(:permutation) - begin - require 'enumerator' - require 'permutation' - class Array - def permutation - Permutation.for(self).to_enum.map { |x| x.project } - end - end - rescue LoadError - warn "Skipping permutation tests." - end -end - -class TestJSON < Test::Unit::TestCase - include JSON - - def setup - @ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do - |x| [x] - end - @ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null", - "[1,-2,3]", "false", "true"].map do - |x| "[#{x}]" - end - @hash = { - 'a' => 2, - 'b' => 3.141, - 'c' => 'c', - 'd' => [ 1, "b", 3.14 ], - 'e' => { 'foo' => 'bar' }, - 'g' => "\"\0\037", - 'h' => 1000.0, - 'i' => 0.001 - } - @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\ - '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}' - end - - def test_construction - parser = JSON::Parser.new('test') - assert_equal 'test', parser.source - end - - def assert_equal_float(expected, is) - assert_in_delta(expected.first, is.first, 1e-2) - end - - def test_parse_simple_arrays - assert_equal([], parse('[]')) - assert_equal([], parse(' [ ] ')) - assert_equal([nil], parse('[null]')) - assert_equal([false], parse('[false]')) - assert_equal([true], parse('[true]')) - assert_equal([-23], parse('[-23]')) - assert_equal([23], parse('[23]')) - assert_equal([0.23], parse('[0.23]')) - assert_equal([0.0], parse('[0e0]')) - assert_raises(JSON::ParserError) { parse('[+23.2]') } - assert_raises(JSON::ParserError) { parse('[+23]') } - assert_raises(JSON::ParserError) { parse('[.23]') } - assert_raises(JSON::ParserError) { parse('[023]') } - assert_equal_float [3.141], parse('[3.141]') - assert_equal_float [-3.141], parse('[-3.141]') - assert_equal_float [3.141], parse('[3141e-3]') - assert_equal_float [3.141], parse('[3141.1e-3]') - assert_equal_float [3.141], parse('[3141E-3]') - assert_equal_float [3.141], parse('[3141.0E-3]') - assert_equal_float [-3.141], parse('[-3141.0e-3]') - assert_equal_float [-3.141], parse('[-3141e-3]') - assert_raises(ParserError) { parse('[NaN]') } - assert parse('[NaN]', :allow_nan => true).first.nan? - assert_raises(ParserError) { parse('[Infinity]') } - assert_equal [1.0/0], parse('[Infinity]', :allow_nan => true) - assert_raises(ParserError) { parse('[-Infinity]') } - assert_equal [-1.0/0], parse('[-Infinity]', :allow_nan => true) - assert_equal([""], parse('[""]')) - assert_equal(["foobar"], parse('["foobar"]')) - assert_equal([{}], parse('[{}]')) - end - - def test_parse_simple_objects - assert_equal({}, parse('{}')) - assert_equal({}, parse(' { } ')) - assert_equal({ "a" => nil }, parse('{ "a" : null}')) - assert_equal({ "a" => nil }, parse('{"a":null}')) - assert_equal({ "a" => false }, parse('{ "a" : false } ')) - assert_equal({ "a" => false }, parse('{"a":false}')) - assert_raises(JSON::ParserError) { parse('{false}') } - assert_equal({ "a" => true }, parse('{"a":true}')) - assert_equal({ "a" => true }, parse(' { "a" : true } ')) - assert_equal({ "a" => -23 }, parse(' { "a" : -23 } ')) - assert_equal({ "a" => -23 }, parse(' { "a" : -23 } ')) - assert_equal({ "a" => 23 }, parse('{"a":23 } ')) - assert_equal({ "a" => 23 }, parse(' { "a" : 23 } ')) - assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) - assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) - end - - def test_parse_json_primitive_values - assert_raise(JSON::ParserError) { JSON.parse('') } - assert_raise(JSON::ParserError) { JSON.parse('', :quirks_mode => true) } - assert_raise(TypeError) { JSON::Parser.new(nil).parse } - assert_raise(TypeError) { JSON::Parser.new(nil, :quirks_mode => true).parse } - assert_raise(TypeError) { JSON.parse(nil) } - assert_raise(TypeError) { JSON.parse(nil, :quirks_mode => true) } - assert_raise(JSON::ParserError) { JSON.parse(' /* foo */ ') } - assert_raise(JSON::ParserError) { JSON.parse(' /* foo */ ', :quirks_mode => true) } - parser = JSON::Parser.new('null') - assert_equal false, parser.quirks_mode? - assert_raise(JSON::ParserError) { parser.parse } - assert_raise(JSON::ParserError) { JSON.parse('null') } - assert_equal nil, JSON.parse('null', :quirks_mode => true) - parser = JSON::Parser.new('null', :quirks_mode => true) - assert_equal true, parser.quirks_mode? - assert_equal nil, parser.parse - assert_raise(JSON::ParserError) { JSON.parse('false') } - assert_equal false, JSON.parse('false', :quirks_mode => true) - assert_raise(JSON::ParserError) { JSON.parse('true') } - assert_equal true, JSON.parse('true', :quirks_mode => true) - assert_raise(JSON::ParserError) { JSON.parse('23') } - assert_equal 23, JSON.parse('23', :quirks_mode => true) - assert_raise(JSON::ParserError) { JSON.parse('1') } - assert_equal 1, JSON.parse('1', :quirks_mode => true) - assert_raise(JSON::ParserError) { JSON.parse('3.141') } - assert_in_delta 3.141, JSON.parse('3.141', :quirks_mode => true), 1E-3 - assert_raise(JSON::ParserError) { JSON.parse('18446744073709551616') } - assert_equal 2 ** 64, JSON.parse('18446744073709551616', :quirks_mode => true) - assert_raise(JSON::ParserError) { JSON.parse('"foo"') } - assert_equal 'foo', JSON.parse('"foo"', :quirks_mode => true) - assert_raise(JSON::ParserError) { JSON.parse('NaN', :allow_nan => true) } - assert JSON.parse('NaN', :quirks_mode => true, :allow_nan => true).nan? - assert_raise(JSON::ParserError) { JSON.parse('Infinity', :allow_nan => true) } - assert JSON.parse('Infinity', :quirks_mode => true, :allow_nan => true).infinite? - assert_raise(JSON::ParserError) { JSON.parse('-Infinity', :allow_nan => true) } - assert JSON.parse('-Infinity', :quirks_mode => true, :allow_nan => true).infinite? - assert_raise(JSON::ParserError) { JSON.parse('[ 1, ]', :quirks_mode => true) } - end - - if Array.method_defined?(:permutation) - def test_parse_more_complex_arrays - a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] - a.permutation.each do |perm| - json = pretty_generate(perm) - assert_equal perm, parse(json) - end - end - - def test_parse_complex_objects - a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] - a.permutation.each do |perm| - s = "a" - orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h } - json = pretty_generate(orig_obj) - assert_equal orig_obj, parse(json) - end - end - end - - def test_parse_arrays - assert_equal([1,2,3], parse('[1,2,3]')) - assert_equal([1.2,2,3], parse('[1.2,2,3]')) - assert_equal([[],[[],[]]], parse('[[],[[],[]]]')) - end - - def test_parse_values - assert_equal([""], parse('[""]')) - assert_equal(["\\"], parse('["\\\\"]')) - assert_equal(['"'], parse('["\""]')) - assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]')) - assert_equal(["\"\b\n\r\t\0\037"], - parse('["\"\b\n\r\t\u0000\u001f"]')) - for i in 0 ... @ary.size - assert_equal(@ary[i], parse(@ary_to_parse[i])) - end - end - - def test_parse_array - assert_equal([], parse('[]')) - assert_equal([], parse(' [ ] ')) - assert_equal([1], parse('[1]')) - assert_equal([1], parse(' [ 1 ] ')) - assert_equal(@ary, - parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]]'\ - ',[false],[true]]')) - assert_equal(@ary, parse(%Q{ [ [1] , ["foo"] , [3.14] \t , [47.11e+2]\s - , [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] })) - end - - class SubArray < Array - def <<(v) - @shifted = true - super - end - - def shifted? - @shifted - end - end - - class SubArray2 < Array - def to_json(*a) - { - JSON.create_id => self.class.name, - 'ary' => to_a, - }.to_json(*a) - end - - def self.json_create(o) - o.delete JSON.create_id - o['ary'] - end - end - - class SubArrayWrapper - def initialize - @data = [] - end - - attr_reader :data - - def [](index) - @data[index] - end - - def <<(value) - @data << value - @shifted = true - end - - def shifted? - @shifted - end - end - - def test_parse_array_custom_array_derived_class - res = parse('[1,2]', :array_class => SubArray) - assert_equal([1,2], res) - assert_equal(SubArray, res.class) - assert res.shifted? - end - - def test_parse_array_custom_non_array_derived_class - res = parse('[1,2]', :array_class => SubArrayWrapper) - assert_equal([1,2], res.data) - assert_equal(SubArrayWrapper, res.class) - assert res.shifted? - end - - def test_parse_object - assert_equal({}, parse('{}')) - assert_equal({}, parse(' { } ')) - assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}')) - assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } ')) - end - - class SubHash < Hash - def []=(k, v) - @item_set = true - super - end - - def item_set? - @item_set - end - end - - class SubHash2 < Hash - def to_json(*a) - { - JSON.create_id => self.class.name, - }.merge(self).to_json(*a) - end - - def self.json_create(o) - o.delete JSON.create_id - self[o] - end - end - - class SubOpenStruct < OpenStruct - def [](k) - __send__(k) - end - - def []=(k, v) - @item_set = true - __send__("#{k}=", v) - end - - def item_set? - @item_set - end - end - - def test_parse_object_custom_hash_derived_class - res = parse('{"foo":"bar"}', :object_class => SubHash) - assert_equal({"foo" => "bar"}, res) - assert_equal(SubHash, res.class) - assert res.item_set? - end - - def test_parse_object_custom_non_hash_derived_class - res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) - assert_equal "bar", res.foo - assert_equal(SubOpenStruct, res.class) - assert res.item_set? - end - - def test_parse_generic_object - res = parse('{"foo":"bar", "baz":{}}', :object_class => JSON::GenericObject) - assert_equal(JSON::GenericObject, res.class) - assert_equal "bar", res.foo - assert_equal "bar", res["foo"] - assert_equal "bar", res[:foo] - assert_equal "bar", res.to_hash[:foo] - assert_equal(JSON::GenericObject, res.baz.class) - end - - def test_generate_core_subclasses_with_new_to_json - obj = SubHash2["foo" => SubHash2["bar" => true]] - obj_json = JSON(obj) - obj_again = JSON.parse(obj_json, :create_additions => true) - assert_kind_of SubHash2, obj_again - assert_kind_of SubHash2, obj_again['foo'] - assert obj_again['foo']['bar'] - assert_equal obj, obj_again - assert_equal ["foo"], JSON(JSON(SubArray2["foo"]), :create_additions => true) - end - - def test_generate_core_subclasses_with_default_to_json - assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"]) - assert_equal '["foo"]', JSON(SubArray["foo"]) - end - - def test_generate_of_core_subclasses - obj = SubHash["foo" => SubHash["bar" => true]] - obj_json = JSON(obj) - obj_again = JSON(obj_json) - assert_kind_of Hash, obj_again - assert_kind_of Hash, obj_again['foo'] - assert obj_again['foo']['bar'] - assert_equal obj, obj_again - end - - def test_parser_reset - parser = Parser.new(@json) - assert_equal(@hash, parser.parse) - assert_equal(@hash, parser.parse) - end - - def test_comments - json = <<EOT -{ - "key1":"value1", // eol comment - "key2":"value2" /* multi line - * comment */, - "key3":"value3" /* multi line - // nested eol comment - * comment */ -} -EOT - assert_equal( - { "key1" => "value1", "key2" => "value2", "key3" => "value3" }, - parse(json)) - json = <<EOT -{ - "key1":"value1" /* multi line - // nested eol comment - /* illegal nested multi line comment */ - * comment */ -} -EOT - assert_raises(ParserError) { parse(json) } - json = <<EOT -{ - "key1":"value1" /* multi line - // nested eol comment - closed multi comment */ - and again, throw an Error */ -} -EOT - assert_raises(ParserError) { parse(json) } - json = <<EOT -{ - "key1":"value1" /*/*/ -} -EOT - assert_equal({ "key1" => "value1" }, parse(json)) - end - - def test_backslash - data = [ '\\.(?i:gif|jpe?g|png)$' ] - json = '["\\\\.(?i:gif|jpe?g|png)$"]' - assert_equal json, JSON.generate(data) - assert_equal data, JSON.parse(json) - # - data = [ '\\"' ] - json = '["\\\\\""]' - assert_equal json, JSON.generate(data) - assert_equal data, JSON.parse(json) - # - json = '["/"]' - data = JSON.parse(json) - assert_equal ['/'], data - assert_equal json, JSON.generate(data) - # - json = '["\""]' - data = JSON.parse(json) - assert_equal ['"'], data - assert_equal json, JSON.generate(data) - json = '["\\\'"]' - data = JSON.parse(json) - assert_equal ["'"], data - assert_equal '["\'"]', JSON.generate(data) - end - - def test_wrong_inputs - assert_raises(ParserError) { JSON.parse('"foo"') } - assert_raises(ParserError) { JSON.parse('123') } - assert_raises(ParserError) { JSON.parse('[] bla') } - assert_raises(ParserError) { JSON.parse('[] 1') } - assert_raises(ParserError) { JSON.parse('[] []') } - assert_raises(ParserError) { JSON.parse('[] {}') } - assert_raises(ParserError) { JSON.parse('{} []') } - assert_raises(ParserError) { JSON.parse('{} {}') } - assert_raises(ParserError) { JSON.parse('[NULL]') } - assert_raises(ParserError) { JSON.parse('[FALSE]') } - assert_raises(ParserError) { JSON.parse('[TRUE]') } - assert_raises(ParserError) { JSON.parse('[07] ') } - assert_raises(ParserError) { JSON.parse('[0a]') } - assert_raises(ParserError) { JSON.parse('[1.]') } - assert_raises(ParserError) { JSON.parse(' ') } - end - - def test_nesting - assert_raises(JSON::NestingError) { JSON.parse '[[]]', :max_nesting => 1 } - assert_raises(JSON::NestingError) { JSON.parser.new('[[]]', :max_nesting => 1).parse } - assert_equal [[]], JSON.parse('[[]]', :max_nesting => 2) - too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' - too_deep_ary = eval too_deep - assert_raises(JSON::NestingError) { JSON.parse too_deep } - assert_raises(JSON::NestingError) { JSON.parser.new(too_deep).parse } - assert_raises(JSON::NestingError) { JSON.parse too_deep, :max_nesting => 100 } - ok = JSON.parse too_deep, :max_nesting => 101 - assert_equal too_deep_ary, ok - ok = JSON.parse too_deep, :max_nesting => nil - assert_equal too_deep_ary, ok - ok = JSON.parse too_deep, :max_nesting => false - assert_equal too_deep_ary, ok - ok = JSON.parse too_deep, :max_nesting => 0 - assert_equal too_deep_ary, ok - assert_raises(JSON::NestingError) { JSON.generate [[]], :max_nesting => 1 } - assert_equal '[[]]', JSON.generate([[]], :max_nesting => 2) - assert_raises(JSON::NestingError) { JSON.generate too_deep_ary } - assert_raises(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 } - ok = JSON.generate too_deep_ary, :max_nesting => 101 - assert_equal too_deep, ok - ok = JSON.generate too_deep_ary, :max_nesting => nil - assert_equal too_deep, ok - ok = JSON.generate too_deep_ary, :max_nesting => false - assert_equal too_deep, ok - ok = JSON.generate too_deep_ary, :max_nesting => 0 - assert_equal too_deep, ok - end - - def test_symbolize_names - assert_equal({ "foo" => "bar", "baz" => "quux" }, - JSON.parse('{"foo":"bar", "baz":"quux"}')) - assert_equal({ :foo => "bar", :baz => "quux" }, - JSON.parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true)) - end - - def test_load - assert_equal @hash, JSON.load(@json) - tempfile = Tempfile.open('json') - tempfile.write @json - tempfile.rewind - assert_equal @hash, JSON.load(tempfile) - stringio = StringIO.new(@json) - stringio.rewind - assert_equal @hash, JSON.load(stringio) - assert_equal nil, JSON.load(nil) - assert_equal nil, JSON.load('') - ensure - tempfile.close! - end - - def test_load_with_options - small_hash = JSON("foo" => 'bar') - symbol_hash = { :foo => 'bar' } - assert_equal symbol_hash, JSON.load(small_hash, nil, :symbolize_names => true) - end - - def test_dump - too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' - assert_equal too_deep, JSON.dump(eval(too_deep)) - assert_kind_of String, Marshal.dump(eval(too_deep)) - assert_raises(ArgumentError) { JSON.dump(eval(too_deep), 100) } - assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 100) } - assert_equal too_deep, JSON.dump(eval(too_deep), 101) - assert_kind_of String, Marshal.dump(eval(too_deep), 101) - output = StringIO.new - JSON.dump(eval(too_deep), output) - assert_equal too_deep, output.string - output = StringIO.new - JSON.dump(eval(too_deep), output, 101) - assert_equal too_deep, output.string - end - - def test_dump_should_modify_defaults - max_nesting = JSON.dump_default_options[:max_nesting] - JSON.dump([], StringIO.new, 10) - assert_equal max_nesting, JSON.dump_default_options[:max_nesting] - end - - def test_big_integers - json1 = JSON([orig = (1 << 31) - 1]) - assert_equal orig, JSON[json1][0] - json2 = JSON([orig = 1 << 31]) - assert_equal orig, JSON[json2][0] - json3 = JSON([orig = (1 << 62) - 1]) - assert_equal orig, JSON[json3][0] - json4 = JSON([orig = 1 << 62]) - assert_equal orig, JSON[json4][0] - json5 = JSON([orig = 1 << 64]) - assert_equal orig, JSON[json5][0] - end - - if defined?(JSON::Ext::Parser) - def test_allocate - parser = JSON::Ext::Parser.new("{}") - assert_raise(TypeError, '[ruby-core:35079]') {parser.__send__(:initialize, "{}")} - parser = JSON::Ext::Parser.allocate - assert_raise(TypeError, '[ruby-core:35079]') {parser.source} - end - end - - def test_argument_encoding - source = "{}".force_encoding("ascii-8bit") - JSON::Parser.new(source) - assert_equal Encoding::ASCII_8BIT, source.encoding - end if defined?(Encoding::ASCII_8BIT) -end diff --git a/tests/test_json_encoding.rb b/tests/test_json_encoding.rb deleted file mode 100644 index fa7d878..0000000 --- a/tests/test_json_encoding.rb +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env ruby -# encoding: utf-8 - -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') - -class TestJSONEncoding < Test::Unit::TestCase - include JSON - - def setup - @utf_8 = '["© ≠ €!"]' - @parsed = [ "© ≠ €!" ] - @generated = '["\u00a9 \u2260 \u20ac!"]' - if String.method_defined?(:encode) - @utf_16_data = [@parsed.first.encode('utf-16be', 'utf-8')] - @utf_8_ascii_8bit = @utf_8.dup.force_encoding(Encoding::ASCII_8BIT) - @utf_16be = @utf_8.encode('utf-16be', 'utf-8') - @utf_16be_ascii_8bit = @utf_16be.dup.force_encoding(Encoding::ASCII_8BIT) - @utf_16le = @utf_8.encode('utf-16le', 'utf-8') - @utf_16le_ascii_8bit = @utf_16le.dup.force_encoding(Encoding::ASCII_8BIT) - @utf_32be = @utf_8.encode('utf-32be', 'utf-8') - @utf_32be_ascii_8bit = @utf_32be.dup.force_encoding(Encoding::ASCII_8BIT) - @utf_32le = @utf_8.encode('utf-32le', 'utf-8') - @utf_32le_ascii_8bit = @utf_32le.dup.force_encoding(Encoding::ASCII_8BIT) - else - require 'iconv' - @utf_16_data = Iconv.iconv('utf-16be', 'utf-8', @parsed.first) - @utf_8_ascii_8bit = @utf_8.dup - @utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8) - @utf_16be_ascii_8bit = @utf_16be.dup - @utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8) - @utf_16le_ascii_8bit = @utf_16le.dup - @utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8) - @utf_32be_ascii_8bit = @utf_32be.dup - @utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8) - @utf_32le_ascii_8bit = @utf_32le.dup - end - end - - def test_parse - assert_equal @parsed, JSON.parse(@utf_8) - assert_equal @parsed, JSON.parse(@utf_16be) - assert_equal @parsed, JSON.parse(@utf_16le) - assert_equal @parsed, JSON.parse(@utf_32be) - assert_equal @parsed, JSON.parse(@utf_32le) - end - - def test_parse_ascii_8bit - assert_equal @parsed, JSON.parse(@utf_8_ascii_8bit) - assert_equal @parsed, JSON.parse(@utf_16be_ascii_8bit) - assert_equal @parsed, JSON.parse(@utf_16le_ascii_8bit) - assert_equal @parsed, JSON.parse(@utf_32be_ascii_8bit) - assert_equal @parsed, JSON.parse(@utf_32le_ascii_8bit) - end - - def test_generate - assert_equal @generated, JSON.generate(@parsed, :ascii_only => true) - if defined?(::Encoding) - assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true) - else - # XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only - assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) } - end - end -end diff --git a/tests/test_json_string_matching.rb b/tests/test_json_string_matching.rb deleted file mode 100644 index c233df8..0000000 --- a/tests/test_json_string_matching.rb +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env ruby -# encoding: utf-8 - -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') -require 'stringio' -require 'time' - -class TestJSONStringMatching < Test::Unit::TestCase - include JSON - - class TestTime < ::Time - def self.json_create(string) - Time.parse(string) - end - - def to_json(*) - %{"#{strftime('%FT%T%z')}"} - end - - def ==(other) - to_i == other.to_i - end - end - - def test_match_date - t = TestTime.new - t_json = [ t ].to_json - assert_equal [ t ], - JSON.parse(t_json, :create_additions => true, - :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime }) - assert_equal [ t.strftime('%FT%T%z') ], - JSON.parse(t_json, :create_additions => true, - :match_string => { /\A\d{3}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime }) - assert_equal [ t.strftime('%FT%T%z') ], - JSON.parse(t_json, - :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime }) - end -end diff --git a/tests/test_json_unicode.rb b/tests/test_json_unicode.rb deleted file mode 100755 index 8352d5c..0000000 --- a/tests/test_json_unicode.rb +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env ruby -# encoding: utf-8 - -require 'test/unit' -require File.join(File.dirname(__FILE__), 'setup_variant') - -class TestJSONUnicode < Test::Unit::TestCase - include JSON - - def test_unicode - assert_equal '""', ''.to_json - assert_equal '"\\b"', "\b".to_json - assert_equal '"\u0001"', 0x1.chr.to_json - assert_equal '"\u001f"', 0x1f.chr.to_json - assert_equal '" "', ' '.to_json - assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json - utf8 = [ "© ≠ €! \01" ] - json = '["© ≠ €! \u0001"]' - assert_equal json, utf8.to_json(:ascii_only => false) - assert_equal utf8, parse(json) - json = '["\u00a9 \u2260 \u20ac! \u0001"]' - assert_equal json, utf8.to_json(:ascii_only => true) - assert_equal utf8, parse(json) - utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"] - json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]" - assert_equal utf8, parse(json) - assert_equal json, utf8.to_json(:ascii_only => false) - utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"] - assert_equal utf8, parse(json) - json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]" - assert_equal json, utf8.to_json(:ascii_only => true) - assert_equal utf8, parse(json) - utf8 = ['საქართველო'] - json = '["საქართველო"]' - assert_equal json, utf8.to_json(:ascii_only => false) - json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]" - assert_equal json, utf8.to_json(:ascii_only => true) - assert_equal utf8, parse(json) - assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false) - assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true) - assert_equal ["€"], JSON.parse('["\u20ac"]') - utf8 = ["\xf0\xa0\x80\x81"] - json = "[\"\xf0\xa0\x80\x81\"]" - assert_equal json, JSON.generate(utf8, :ascii_only => false) - assert_equal utf8, JSON.parse(json) - json = '["\ud840\udc01"]' - assert_equal json, JSON.generate(utf8, :ascii_only => true) - assert_equal utf8, JSON.parse(json) - end - - def test_chars - (0..0x7f).each do |i| - json = '["\u%04x"]' % i - if RUBY_VERSION >= "1.9." - i = i.chr - end - assert_equal i, JSON.parse(json).first[0] - if i == ?\b - generated = JSON.generate(["" << i]) - assert '["\b"]' == generated || '["\10"]' == generated - elsif [?\n, ?\r, ?\t, ?\f].include?(i) - assert_equal '[' << ('' << i).dump << ']', JSON.generate(["" << i]) - elsif i.chr < 0x20.chr - assert_equal json, JSON.generate(["" << i]) - end - end - assert_raise(JSON::GeneratorError) do - JSON.generate(["\x80"], :ascii_only => true) - end - assert_equal "\302\200", JSON.parse('["\u0080"]').first - end -end |