зеркало из https://github.com/github/ruby.git
* ext/json/*, test/json/*: Reverted r50231. Because it's not works with
cross-compile environment. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50267 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
7b14512bee
Коммит
2e4f0af00f
|
@ -1,3 +1,8 @@
|
|||
Sun Apr 12 17:35:17 2015 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
|
||||
|
||||
* ext/json/*, test/json/*: Reverted r50231. Because it's not works with
|
||||
cross-compile environment.
|
||||
|
||||
Sun Apr 12 15:34:59 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* parse.y (arg): fix segfault by null caused by syntax error.
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
# gem base directory versioning file [executable files under bin]
|
||||
rdoc lib/rdoc lib/rdoc.rb [rdoc ri]
|
||||
json ext/json ext/json/lib/json/version.rb
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
require 'mkmf'
|
||||
create_makefile('json')
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
|
||||
#ifndef _FBUFFER_H_
|
||||
#define _FBUFFER_H_
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
#ifndef RHASH_SIZE
|
||||
#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
|
||||
#endif
|
||||
|
||||
#ifndef RFLOAT_VALUE
|
||||
#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
|
||||
#endif
|
||||
|
||||
#ifndef RARRAY_PTR
|
||||
#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
|
||||
#endif
|
||||
#ifndef RARRAY_LEN
|
||||
#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
|
||||
#endif
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(string) RSTRING(string)->ptr
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(string) RSTRING(string)->len
|
||||
#endif
|
||||
|
||||
#ifdef PRIsVALUE
|
||||
# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
|
||||
# define RB_OBJ_STRING(obj) (obj)
|
||||
#else
|
||||
# define PRIsVALUE "s"
|
||||
# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
|
||||
# define RB_OBJ_STRING(obj) StringValueCStr(obj)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
#include "ruby/encoding.h"
|
||||
#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
|
||||
#else
|
||||
#define FORCE_UTF8(obj)
|
||||
#endif
|
||||
|
||||
/* We don't need to guard objects for rbx, so let's do nothing at all. */
|
||||
#ifndef RB_GC_GUARD
|
||||
#define RB_GC_GUARD(object)
|
||||
#endif
|
||||
|
||||
typedef struct FBufferStruct {
|
||||
unsigned long initial_length;
|
||||
char *ptr;
|
||||
unsigned long len;
|
||||
unsigned long capa;
|
||||
} FBuffer;
|
||||
|
||||
#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
|
||||
|
||||
#define FBUFFER_PTR(fb) (fb->ptr)
|
||||
#define FBUFFER_LEN(fb) (fb->len)
|
||||
#define FBUFFER_CAPA(fb) (fb->capa)
|
||||
#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
|
||||
|
||||
static FBuffer *fbuffer_alloc(unsigned long initial_length);
|
||||
static void fbuffer_free(FBuffer *fb);
|
||||
static void fbuffer_clear(FBuffer *fb);
|
||||
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
|
||||
#ifdef JSON_GENERATOR
|
||||
static void fbuffer_append_long(FBuffer *fb, long number);
|
||||
#endif
|
||||
static void fbuffer_append_char(FBuffer *fb, char newchr);
|
||||
#ifdef JSON_GENERATOR
|
||||
static FBuffer *fbuffer_dup(FBuffer *fb);
|
||||
static VALUE fbuffer_to_s(FBuffer *fb);
|
||||
#endif
|
||||
|
||||
static FBuffer *fbuffer_alloc(unsigned long initial_length)
|
||||
{
|
||||
FBuffer *fb;
|
||||
if (initial_length <= 0) initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
|
||||
fb = ALLOC(FBuffer);
|
||||
memset((void *) fb, 0, sizeof(FBuffer));
|
||||
fb->initial_length = initial_length;
|
||||
return fb;
|
||||
}
|
||||
|
||||
static void fbuffer_free(FBuffer *fb)
|
||||
{
|
||||
if (fb->ptr) ruby_xfree(fb->ptr);
|
||||
ruby_xfree(fb);
|
||||
}
|
||||
|
||||
static void fbuffer_clear(FBuffer *fb)
|
||||
{
|
||||
fb->len = 0;
|
||||
}
|
||||
|
||||
static void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
||||
{
|
||||
unsigned long required;
|
||||
|
||||
if (!fb->ptr) {
|
||||
fb->ptr = ALLOC_N(char, fb->initial_length);
|
||||
fb->capa = fb->initial_length;
|
||||
}
|
||||
|
||||
for (required = fb->capa; requested > required - fb->len; required <<= 1);
|
||||
|
||||
if (required > fb->capa) {
|
||||
REALLOC_N(fb->ptr, char, required);
|
||||
fb->capa = required;
|
||||
}
|
||||
}
|
||||
|
||||
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
||||
{
|
||||
if (len > 0) {
|
||||
fbuffer_inc_capa(fb, len);
|
||||
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
||||
fb->len += len;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JSON_GENERATOR
|
||||
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
||||
{
|
||||
const char *newstr = StringValuePtr(str);
|
||||
unsigned long len = RSTRING_LEN(str);
|
||||
|
||||
RB_GC_GUARD(str);
|
||||
|
||||
fbuffer_append(fb, newstr, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void fbuffer_append_char(FBuffer *fb, char newchr)
|
||||
{
|
||||
fbuffer_inc_capa(fb, 1);
|
||||
*(fb->ptr + fb->len) = newchr;
|
||||
fb->len++;
|
||||
}
|
||||
|
||||
#ifdef JSON_GENERATOR
|
||||
static void freverse(char *start, char *end)
|
||||
{
|
||||
char c;
|
||||
|
||||
while (end > start) {
|
||||
c = *end, *end-- = *start, *start++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
static long fltoa(long number, char *buf)
|
||||
{
|
||||
static char digits[] = "0123456789";
|
||||
long sign = number;
|
||||
char* tmp = buf;
|
||||
|
||||
if (sign < 0) number = -number;
|
||||
do *tmp++ = digits[number % 10]; while (number /= 10);
|
||||
if (sign < 0) *tmp++ = '-';
|
||||
freverse(buf, tmp - 1);
|
||||
return tmp - buf;
|
||||
}
|
||||
|
||||
static void fbuffer_append_long(FBuffer *fb, long number)
|
||||
{
|
||||
char buf[20];
|
||||
unsigned long len = fltoa(number, buf);
|
||||
fbuffer_append(fb, buf, len);
|
||||
}
|
||||
|
||||
static FBuffer *fbuffer_dup(FBuffer *fb)
|
||||
{
|
||||
unsigned long len = fb->len;
|
||||
FBuffer *result;
|
||||
|
||||
result = fbuffer_alloc(len);
|
||||
fbuffer_append(result, FBUFFER_PAIR(fb));
|
||||
return result;
|
||||
}
|
||||
|
||||
static VALUE fbuffer_to_s(FBuffer *fb)
|
||||
{
|
||||
VALUE result = rb_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
|
||||
fbuffer_free(fb);
|
||||
FORCE_UTF8(result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
$(OBJS): $(ruby_headers)
|
||||
generator.o: generator.c generator.h $(srcdir)/../fbuffer/fbuffer.h
|
||||
|
||||
# AUTOGENERATED DEPENDENCIES START
|
||||
generator.o: $(RUBY_EXTCONF_H)
|
||||
generator.o: $(arch_hdrdir)/ruby/config.h
|
||||
generator.o: $(hdrdir)/ruby/defines.h
|
||||
generator.o: $(hdrdir)/ruby/encoding.h
|
||||
generator.o: $(hdrdir)/ruby/intern.h
|
||||
generator.o: $(hdrdir)/ruby/missing.h
|
||||
generator.o: $(hdrdir)/ruby/oniguruma.h
|
||||
generator.o: $(hdrdir)/ruby/re.h
|
||||
generator.o: $(hdrdir)/ruby/regex.h
|
||||
generator.o: $(hdrdir)/ruby/ruby.h
|
||||
generator.o: $(hdrdir)/ruby/st.h
|
||||
generator.o: $(hdrdir)/ruby/subst.h
|
||||
generator.o: $(top_srcdir)/ext/json/fbuffer/fbuffer.h
|
||||
generator.o: $(top_srcdir)/include/ruby.h
|
||||
generator.o: generator.c
|
||||
generator.o: generator.h
|
||||
# AUTOGENERATED DEPENDENCIES END
|
|
@ -0,0 +1,4 @@
|
|||
require 'mkmf'
|
||||
|
||||
$defs << "-DJSON_GENERATOR"
|
||||
create_makefile 'json/ext/generator'
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,167 @@
|
|||
#ifndef _GENERATOR_H_
|
||||
#define _GENERATOR_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
#ifdef HAVE_RUBY_RE_H
|
||||
#include "ruby/re.h"
|
||||
#else
|
||||
#include "re.h"
|
||||
#endif
|
||||
|
||||
#ifndef rb_intern_str
|
||||
#define rb_intern_str(string) SYM2ID(rb_str_intern(string))
|
||||
#endif
|
||||
|
||||
#ifndef rb_obj_instance_variables
|
||||
#define rb_obj_instance_variables(object) rb_funcall(object, rb_intern("instance_variables"), 0)
|
||||
#endif
|
||||
|
||||
#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
|
||||
|
||||
/* unicode definitions */
|
||||
|
||||
#define UNI_STRICT_CONVERSION 1
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
#define UNI_MAX_BMP (UTF32)0x0000FFFF
|
||||
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
|
||||
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
|
||||
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
|
||||
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
static const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
static const UTF32 halfBase = 0x0010000UL;
|
||||
static const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length);
|
||||
static void unicode_escape(char *buf, UTF16 character);
|
||||
static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character);
|
||||
static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string);
|
||||
static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string);
|
||||
static char *fstrndup(const char *ptr, unsigned long len);
|
||||
|
||||
/* ruby api and some helpers */
|
||||
|
||||
typedef struct JSON_Generator_StateStruct {
|
||||
char *indent;
|
||||
long indent_len;
|
||||
char *space;
|
||||
long space_len;
|
||||
char *space_before;
|
||||
long space_before_len;
|
||||
char *object_nl;
|
||||
long object_nl_len;
|
||||
char *array_nl;
|
||||
long array_nl_len;
|
||||
FBuffer *array_delim;
|
||||
FBuffer *object_delim;
|
||||
FBuffer *object_delim2;
|
||||
long max_nesting;
|
||||
char allow_nan;
|
||||
char ascii_only;
|
||||
char quirks_mode;
|
||||
long depth;
|
||||
long buffer_initial_length;
|
||||
} JSON_Generator_State;
|
||||
|
||||
#define GET_STATE_TO(self, state) \
|
||||
TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state)
|
||||
|
||||
#define GET_STATE(self) \
|
||||
JSON_Generator_State *state; \
|
||||
GET_STATE_TO(self, state)
|
||||
|
||||
#define GENERATE_JSON(type) \
|
||||
FBuffer *buffer; \
|
||||
VALUE Vstate; \
|
||||
JSON_Generator_State *state; \
|
||||
\
|
||||
rb_scan_args(argc, argv, "01", &Vstate); \
|
||||
Vstate = cState_from_state_s(cState, Vstate); \
|
||||
TypedData_Get_Struct(Vstate, JSON_Generator_State, &JSON_Generator_State_type, state); \
|
||||
buffer = cState_prepare_buffer(Vstate); \
|
||||
generate_json_##type(buffer, Vstate, state, self); \
|
||||
return fbuffer_to_s(buffer)
|
||||
|
||||
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mString_included_s(VALUE self, VALUE modul);
|
||||
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mString_to_json_raw_object(VALUE self);
|
||||
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mString_Extend_json_create(VALUE self, VALUE o);
|
||||
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self);
|
||||
static void State_free(void *state);
|
||||
static JSON_Generator_State *State_allocate(void);
|
||||
static VALUE cState_s_allocate(VALUE klass);
|
||||
static VALUE cState_configure(VALUE self, VALUE opts);
|
||||
static VALUE cState_to_h(VALUE self);
|
||||
static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
|
||||
static VALUE cState_partial_generate(VALUE self, VALUE obj);
|
||||
static VALUE cState_generate(VALUE self, VALUE obj);
|
||||
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
||||
static VALUE cState_indent(VALUE self);
|
||||
static VALUE cState_indent_set(VALUE self, VALUE indent);
|
||||
static VALUE cState_space(VALUE self);
|
||||
static VALUE cState_space_set(VALUE self, VALUE space);
|
||||
static VALUE cState_space_before(VALUE self);
|
||||
static VALUE cState_space_before_set(VALUE self, VALUE space_before);
|
||||
static VALUE cState_object_nl(VALUE self);
|
||||
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl);
|
||||
static VALUE cState_array_nl(VALUE self);
|
||||
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl);
|
||||
static VALUE cState_max_nesting(VALUE self);
|
||||
static VALUE cState_max_nesting_set(VALUE self, VALUE depth);
|
||||
static VALUE cState_allow_nan_p(VALUE self);
|
||||
static VALUE cState_ascii_only_p(VALUE self);
|
||||
static VALUE cState_depth(VALUE self);
|
||||
static VALUE cState_depth_set(VALUE self, VALUE depth);
|
||||
static FBuffer *cState_prepare_buffer(VALUE self);
|
||||
#ifndef ZALLOC
|
||||
#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type)))
|
||||
static inline void *ruby_zalloc(size_t n)
|
||||
{
|
||||
void *p = ruby_xmalloc(n);
|
||||
memset(p, 0, n);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
#ifdef TypedData_Wrap_Struct
|
||||
static const rb_data_type_t JSON_Generator_State_type;
|
||||
#define NEW_TYPEDDATA_WRAPPER 1
|
||||
#else
|
||||
#define TypedData_Wrap_Struct(klass, ignore, json) Data_Wrap_Struct(klass, NULL, State_free, json)
|
||||
#define TypedData_Get_Struct(self, JSON_Generator_State, ignore, json) Data_Get_Struct(self, JSON_Generator_State, json)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
require 'json/common'
|
||||
|
||||
##
|
||||
# = JavaScript Object Notation (JSON)
|
||||
#
|
||||
# JSON is a lightweight data-interchange format. It is easy for us
|
||||
# humans to read and write. Plus, equally simple for machines to generate or parse.
|
||||
# JSON is completely language agnostic, making it the ideal interchange format.
|
||||
#
|
||||
# Built on two universally available structures:
|
||||
# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array.
|
||||
# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list.
|
||||
#
|
||||
# To read more about JSON visit: http://json.org
|
||||
#
|
||||
# == Parsing JSON
|
||||
#
|
||||
# To parse a JSON string received by another application or generated within
|
||||
# your existing application:
|
||||
#
|
||||
# require 'json'
|
||||
#
|
||||
# my_hash = JSON.parse('{"hello": "goodbye"}')
|
||||
# puts my_hash["hello"] => "goodbye"
|
||||
#
|
||||
# Notice the extra quotes <tt>''</tt> around the hash notation. Ruby expects
|
||||
# the argument to be a string and can't convert objects like a hash or array.
|
||||
#
|
||||
# Ruby converts your string into a hash
|
||||
#
|
||||
# == Generating JSON
|
||||
#
|
||||
# Creating a JSON string for communication or serialization is
|
||||
# just as simple.
|
||||
#
|
||||
# require 'json'
|
||||
#
|
||||
# my_hash = {:hello => "goodbye"}
|
||||
# puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}"
|
||||
#
|
||||
# Or an alternative way:
|
||||
#
|
||||
# require 'json'
|
||||
# puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}"
|
||||
#
|
||||
# <tt>JSON.generate</tt> only allows objects or arrays to be converted
|
||||
# to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes
|
||||
# even though it acts only as a method for serialization:
|
||||
#
|
||||
# require 'json'
|
||||
#
|
||||
# 1.to_json => "1"
|
||||
#
|
||||
module JSON
|
||||
require 'json/version'
|
||||
|
||||
begin
|
||||
require 'json/ext'
|
||||
rescue LoadError
|
||||
require 'json/pure'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
defined?(::BigDecimal) or require 'bigdecimal'
|
||||
|
||||
class BigDecimal
|
||||
# Import a JSON Marshalled object.
|
||||
#
|
||||
# method used for JSON marshalling support.
|
||||
def self.json_create(object)
|
||||
BigDecimal._load object['b']
|
||||
end
|
||||
|
||||
# Marshal the object to JSON.
|
||||
#
|
||||
# method used for JSON marshalling support.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'b' => _dump,
|
||||
}
|
||||
end
|
||||
|
||||
# return the JSON value
|
||||
def to_json(*)
|
||||
as_json.to_json
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
defined?(::Complex) or require 'complex'
|
||||
|
||||
class Complex
|
||||
|
||||
# Deserializes JSON string by converting Real value <tt>r</tt>, imaginary
|
||||
# value <tt>i</tt>, to a Complex object.
|
||||
def self.json_create(object)
|
||||
Complex(object['r'], object['i'])
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'r' => real,
|
||||
'i' => imag,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Complex) along with real value <tt>r</tt> and imaginary value <tt>i</tt> as JSON string
|
||||
def to_json(*)
|
||||
as_json.to_json
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# This file requires the implementations of ruby core's custom objects for
|
||||
# serialisation/deserialisation.
|
||||
|
||||
require 'json/add/date'
|
||||
require 'json/add/date_time'
|
||||
require 'json/add/exception'
|
||||
require 'json/add/range'
|
||||
require 'json/add/regexp'
|
||||
require 'json/add/struct'
|
||||
require 'json/add/symbol'
|
||||
require 'json/add/time'
|
|
@ -0,0 +1,34 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
require 'date'
|
||||
|
||||
# Date serialization/deserialization
|
||||
class Date
|
||||
|
||||
# Deserializes JSON string by converting Julian year <tt>y</tt>, month
|
||||
# <tt>m</tt>, day <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> to Date.
|
||||
def self.json_create(object)
|
||||
civil(*object.values_at('y', 'm', 'd', 'sg'))
|
||||
end
|
||||
|
||||
alias start sg unless method_defined?(:start)
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'y' => year,
|
||||
'm' => month,
|
||||
'd' => day,
|
||||
'sg' => start,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Date) with Julian year <tt>y</tt>, month <tt>m</tt>, day
|
||||
# <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
require 'date'
|
||||
|
||||
# DateTime serialization/deserialization
|
||||
class DateTime
|
||||
|
||||
# Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>,
|
||||
# day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>,
|
||||
# offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> to DateTime.
|
||||
def self.json_create(object)
|
||||
args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
|
||||
of_a, of_b = object['of'].split('/')
|
||||
if of_b and of_b != '0'
|
||||
args << Rational(of_a.to_i, of_b.to_i)
|
||||
else
|
||||
args << of_a
|
||||
end
|
||||
args << object['sg']
|
||||
civil(*args)
|
||||
end
|
||||
|
||||
alias start sg unless method_defined?(:start)
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'y' => year,
|
||||
'm' => month,
|
||||
'd' => day,
|
||||
'H' => hour,
|
||||
'M' => min,
|
||||
'S' => sec,
|
||||
'of' => offset.to_s,
|
||||
'sg' => start,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (DateTime) with Julian year <tt>y</tt>, month <tt>m</tt>,
|
||||
# day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>,
|
||||
# offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
# Exception serialization/deserialization
|
||||
class Exception
|
||||
|
||||
# Deserializes JSON string by constructing new Exception object with message
|
||||
# <tt>m</tt> and backtrace <tt>b</tt> serialized with <tt>to_json</tt>
|
||||
def self.json_create(object)
|
||||
result = new(object['m'])
|
||||
result.set_backtrace object['b']
|
||||
result
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'm' => message,
|
||||
'b' => backtrace,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Exception) with message <tt>m</tt> and backtrace array
|
||||
# <tt>b</tt> as JSON string
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
require 'ostruct'
|
||||
|
||||
# OpenStruct serialization/deserialization
|
||||
class OpenStruct
|
||||
|
||||
# Deserializes JSON string by constructing new Struct object with values
|
||||
# <tt>v</tt> serialized by <tt>to_json</tt>.
|
||||
def self.json_create(object)
|
||||
new(object['t'] || object[:t])
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
klass = self.class.name
|
||||
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
|
||||
{
|
||||
JSON.create_id => klass,
|
||||
't' => table,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (OpenStruct) with this struct's values <tt>v</tt> as a
|
||||
# JSON string.
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
# Range serialization/deserialization
|
||||
class Range
|
||||
|
||||
# Deserializes JSON string by constructing new Range object with arguments
|
||||
# <tt>a</tt> serialized by <tt>to_json</tt>.
|
||||
def self.json_create(object)
|
||||
new(*object['a'])
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'a' => [ first, last, exclude_end? ]
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Range) with JSON array of arguments <tt>a</tt> which
|
||||
# include <tt>first</tt> (integer), <tt>last</tt> (integer), and
|
||||
# <tt>exclude_end?</tt> (boolean) as JSON string.
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
defined?(::Rational) or require 'rational'
|
||||
|
||||
class Rational
|
||||
# Deserializes JSON string by converting numerator value <tt>n</tt>,
|
||||
# denominator value <tt>d</tt>, to a Rational object.
|
||||
def self.json_create(object)
|
||||
Rational(object['n'], object['d'])
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'n' => numerator,
|
||||
'd' => denominator,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Rational) along with numerator value <tt>n</tt> and denominator value <tt>d</tt> as JSON string
|
||||
def to_json(*)
|
||||
as_json.to_json
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
# Regexp serialization/deserialization
|
||||
class Regexp
|
||||
|
||||
# Deserializes JSON string by constructing new Regexp object with source
|
||||
# <tt>s</tt> (Regexp or String) and options <tt>o</tt> serialized by
|
||||
# <tt>to_json</tt>
|
||||
def self.json_create(object)
|
||||
new(object['s'], object['o'])
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
'o' => options,
|
||||
's' => source,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Regexp) with options <tt>o</tt> and source <tt>s</tt>
|
||||
# (Regexp or String) as JSON string
|
||||
def to_json(*)
|
||||
as_json.to_json
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
# Struct serialization/deserialization
|
||||
class Struct
|
||||
|
||||
# Deserializes JSON string by constructing new Struct object with values
|
||||
# <tt>v</tt> serialized by <tt>to_json</tt>.
|
||||
def self.json_create(object)
|
||||
new(*object['v'])
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
klass = self.class.name
|
||||
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
|
||||
{
|
||||
JSON.create_id => klass,
|
||||
'v' => values,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Struct) with Struct values <tt>v</tt> as a JSON string.
|
||||
# Only named structs are supported.
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
# Symbol serialization/deserialization
|
||||
class Symbol
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
's' => to_s,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Symbol) with String representation of Symbol as a JSON string.
|
||||
def to_json(*a)
|
||||
as_json.to_json(*a)
|
||||
end
|
||||
|
||||
# Deserializes JSON string by converting the <tt>string</tt> value stored in the object to a Symbol
|
||||
def self.json_create(o)
|
||||
o['s'].to_sym
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
||||
require 'json'
|
||||
end
|
||||
|
||||
# Time serialization/deserialization
|
||||
class Time
|
||||
|
||||
# Deserializes JSON string by converting time since epoch to Time
|
||||
def self.json_create(object)
|
||||
if usec = object.delete('u') # used to be tv_usec -> tv_nsec
|
||||
object['n'] = usec * 1000
|
||||
end
|
||||
if method_defined?(:tv_nsec)
|
||||
at(object['s'], Rational(object['n'], 1000))
|
||||
else
|
||||
at(object['s'], object['n'] / 1000)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a hash, that will be turned into a JSON object and represent this
|
||||
# object.
|
||||
def as_json(*)
|
||||
nanoseconds = [ tv_usec * 1000 ]
|
||||
respond_to?(:tv_nsec) and nanoseconds << tv_nsec
|
||||
nanoseconds = nanoseconds.max
|
||||
{
|
||||
JSON.create_id => self.class.name,
|
||||
's' => tv_sec,
|
||||
'n' => nanoseconds,
|
||||
}
|
||||
end
|
||||
|
||||
# Stores class name (Time) with number of seconds since epoch and number of
|
||||
# microseconds for Time as JSON string
|
||||
def to_json(*args)
|
||||
as_json.to_json(*args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,484 @@
|
|||
require 'json/version'
|
||||
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.
|
||||
#
|
||||
# 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)
|
||||
else
|
||||
JSON.generate(object, opts)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the JSON parser class that is used by JSON. This is either
|
||||
# JSON::Ext::Parser or JSON::Pure::Parser.
|
||||
attr_reader :parser
|
||||
|
||||
# Set the JSON parser class _parser_ to be used by JSON.
|
||||
def parser=(parser) # :nodoc:
|
||||
@parser = parser
|
||||
remove_const :Parser if JSON.const_defined_in?(self, :Parser)
|
||||
const_set :Parser, parser
|
||||
end
|
||||
|
||||
# Return the constant located at _path_. The format of _path_ has to be
|
||||
# either ::A::B::C or A::B::C. In any case, A has to be located at the top
|
||||
# level (absolute namespace path?). If there doesn't exist a constant at
|
||||
# the given path, an ArgumentError is raised.
|
||||
def deep_const_get(path) # :nodoc:
|
||||
path.to_s.split(/::/).inject(Object) do |p, c|
|
||||
case
|
||||
when c.empty? then p
|
||||
when JSON.const_defined_in?(p, c) then p.const_get(c)
|
||||
else
|
||||
begin
|
||||
p.const_missing(c)
|
||||
rescue NameError => e
|
||||
raise ArgumentError, "can't get const #{path}: #{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Set the module _generator_ to be used by JSON.
|
||||
def generator=(generator) # :nodoc:
|
||||
old, $VERBOSE = $VERBOSE, nil
|
||||
@generator = generator
|
||||
generator_methods = generator::GeneratorMethods
|
||||
for const in generator_methods.constants
|
||||
klass = deep_const_get(const)
|
||||
modul = generator_methods.const_get(const)
|
||||
klass.class_eval do
|
||||
instance_methods(false).each do |m|
|
||||
m.to_s == 'to_json' and remove_method m
|
||||
end
|
||||
include modul
|
||||
end
|
||||
end
|
||||
self.state = generator::State
|
||||
const_set :State, self.state
|
||||
const_set :SAFE_STATE_PROTOTYPE, State.new
|
||||
const_set :FAST_STATE_PROTOTYPE, State.new(
|
||||
:indent => '',
|
||||
:space => '',
|
||||
:object_nl => "",
|
||||
:array_nl => "",
|
||||
:max_nesting => false
|
||||
)
|
||||
const_set :PRETTY_STATE_PROTOTYPE, State.new(
|
||||
:indent => ' ',
|
||||
:space => ' ',
|
||||
:object_nl => "\n",
|
||||
:array_nl => "\n"
|
||||
)
|
||||
ensure
|
||||
$VERBOSE = old
|
||||
end
|
||||
|
||||
# Returns the JSON generator module that is used by JSON. This is
|
||||
# either JSON::Ext::Generator or JSON::Pure::Generator.
|
||||
attr_reader :generator
|
||||
|
||||
# Returns the JSON generator state class that is used by JSON. This is
|
||||
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
|
||||
attr_accessor :state
|
||||
|
||||
# This is create identifier, which is used to decide if the _json_create_
|
||||
# hook of a class should be called. It defaults to 'json_class'.
|
||||
attr_accessor :create_id
|
||||
end
|
||||
self.create_id = 'json_class'
|
||||
|
||||
NaN = 0.0/0
|
||||
|
||||
Infinity = 1.0/0
|
||||
|
||||
MinusInfinity = -Infinity
|
||||
|
||||
# The base exception for JSON errors.
|
||||
class JSONError < StandardError
|
||||
def self.wrap(exception)
|
||||
obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
|
||||
obj.set_backtrace exception.backtrace
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
# This exception is raised if a parser error occurs.
|
||||
class ParserError < JSONError; end
|
||||
|
||||
# This exception is raised if the nesting of parsed data structures is too
|
||||
# deep.
|
||||
class NestingError < ParserError; end
|
||||
|
||||
# :stopdoc:
|
||||
class CircularDatastructure < NestingError; end
|
||||
# :startdoc:
|
||||
|
||||
# This exception is raised if a generator or unparser error occurs.
|
||||
class GeneratorError < JSONError; end
|
||||
# For backwards compatibility
|
||||
UnparserError = GeneratorError
|
||||
|
||||
# This exception is raised if the required unicode support is missing on the
|
||||
# system. Usually this means that the iconv library is not installed.
|
||||
class MissingUnicodeSupport < JSONError; end
|
||||
|
||||
module_function
|
||||
|
||||
# Parse the JSON document _source_ into a Ruby data structure and return it.
|
||||
#
|
||||
# _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.
|
||||
# * *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.
|
||||
# * *symbolize_names*: If set to true, returns symbols for the names
|
||||
# (keys) in a JSON object. Otherwise strings are returned. Strings are
|
||||
# the default.
|
||||
# * *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.
|
||||
# * *object_class*: Defaults to Hash
|
||||
# * *array_class*: Defaults to Array
|
||||
def parse(source, opts = {})
|
||||
Parser.new(source, opts).parse
|
||||
end
|
||||
|
||||
# Parse the JSON document _source_ into a Ruby data structure and return it.
|
||||
# The bang version of the parse method defaults to the more dangerous values
|
||||
# for the _opts_ hash, so be sure only to parse trusted _source_ documents.
|
||||
#
|
||||
# _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.
|
||||
# * *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.
|
||||
# * *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.
|
||||
def parse!(source, opts = {})
|
||||
opts = {
|
||||
:max_nesting => false,
|
||||
:allow_nan => true
|
||||
}.update(opts)
|
||||
Parser.new(source, opts).parse
|
||||
end
|
||||
|
||||
# Generate a JSON document from the Ruby data structure _obj_ and return
|
||||
# it. _state_ is * a JSON::State object,
|
||||
# * or a Hash like object (responding to to_hash),
|
||||
# * an object convertible into a hash by a to_h method,
|
||||
# that is used as or to configure a State object.
|
||||
#
|
||||
# It defaults to a state object, that creates the shortest possible JSON text
|
||||
# in one line, checks for circular data structures and doesn't allow NaN,
|
||||
# Infinity, and -Infinity.
|
||||
#
|
||||
# A _state_ hash can have the following keys:
|
||||
# * *indent*: a string used to indent levels (default: ''),
|
||||
# * *space*: a string that is put after, a : or , delimiter (default: ''),
|
||||
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
||||
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
||||
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
||||
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
||||
# generated, otherwise an exception is thrown if these values are
|
||||
# encountered. This options defaults to false.
|
||||
# * *max_nesting*: The maximum depth of nesting allowed in the data
|
||||
# structures from which JSON is to be generated. Disable depth checking
|
||||
# with :max_nesting => false, it defaults to 100.
|
||||
#
|
||||
# See also the fast_generate for the fastest creation method with the least
|
||||
# amount of sanity checks, and the pretty_generate method for some
|
||||
# defaults for pretty output.
|
||||
def generate(obj, opts = nil)
|
||||
if State === opts
|
||||
state, opts = opts, nil
|
||||
else
|
||||
state = SAFE_STATE_PROTOTYPE.dup
|
||||
end
|
||||
if opts
|
||||
if opts.respond_to? :to_hash
|
||||
opts = opts.to_hash
|
||||
elsif opts.respond_to? :to_h
|
||||
opts = opts.to_h
|
||||
else
|
||||
raise TypeError, "can't convert #{opts.class} into Hash"
|
||||
end
|
||||
state = state.configure(opts)
|
||||
end
|
||||
state.generate(obj)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
# I want to deprecate these later, so I'll first be silent about them, and
|
||||
# later delete them.
|
||||
alias unparse generate
|
||||
module_function :unparse
|
||||
# :startdoc:
|
||||
|
||||
# Generate a JSON document from the Ruby data structure _obj_ and return it.
|
||||
# This method disables the checks for circles in Ruby objects.
|
||||
#
|
||||
# *WARNING*: Be careful not to pass any Ruby data structures with circles as
|
||||
# _obj_ argument because this will cause JSON to go into an infinite loop.
|
||||
def fast_generate(obj, opts = nil)
|
||||
if State === opts
|
||||
state, opts = opts, nil
|
||||
else
|
||||
state = FAST_STATE_PROTOTYPE.dup
|
||||
end
|
||||
if opts
|
||||
if opts.respond_to? :to_hash
|
||||
opts = opts.to_hash
|
||||
elsif opts.respond_to? :to_h
|
||||
opts = opts.to_h
|
||||
else
|
||||
raise TypeError, "can't convert #{opts.class} into Hash"
|
||||
end
|
||||
state.configure(opts)
|
||||
end
|
||||
state.generate(obj)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
|
||||
alias fast_unparse fast_generate
|
||||
module_function :fast_unparse
|
||||
# :startdoc:
|
||||
|
||||
# Generate a JSON document from the Ruby data structure _obj_ and return it.
|
||||
# The returned document is a prettier form of the document returned by
|
||||
# #unparse.
|
||||
#
|
||||
# The _opts_ argument can be used to configure the generator. See the
|
||||
# generate method for a more detailed explanation.
|
||||
def pretty_generate(obj, opts = nil)
|
||||
if State === opts
|
||||
state, opts = opts, nil
|
||||
else
|
||||
state = PRETTY_STATE_PROTOTYPE.dup
|
||||
end
|
||||
if opts
|
||||
if opts.respond_to? :to_hash
|
||||
opts = opts.to_hash
|
||||
elsif opts.respond_to? :to_h
|
||||
opts = opts.to_h
|
||||
else
|
||||
raise TypeError, "can't convert #{opts.class} into Hash"
|
||||
end
|
||||
state.configure(opts)
|
||||
end
|
||||
state.generate(obj)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
|
||||
alias pretty_unparse pretty_generate
|
||||
module_function :pretty_unparse
|
||||
# :startdoc:
|
||||
|
||||
class << self
|
||||
# The global default options for the JSON.load method:
|
||||
# :max_nesting: false
|
||||
# :allow_nan: true
|
||||
# :quirks_mode: true
|
||||
attr_accessor :load_default_options
|
||||
end
|
||||
self.load_default_options = {
|
||||
:max_nesting => false,
|
||||
:allow_nan => true,
|
||||
:quirks_mode => true,
|
||||
:create_additions => true,
|
||||
}
|
||||
|
||||
# Load a ruby data structure from a JSON _source_ and return it. A source can
|
||||
# either be a string-like object, an IO-like object, or an object responding
|
||||
# to the read method. If _proc_ was given, it will be called with any nested
|
||||
# Ruby object as an argument recursively in depth first order. To modify the
|
||||
# default options pass in the optional _options_ argument as well.
|
||||
#
|
||||
# BEWARE: This method is meant to serialise data from trusted user input,
|
||||
# like from your own database server or clients under your control, it could
|
||||
# be dangerous to allow untrusted users to pass JSON sources into it. The
|
||||
# default options for the parser can be changed via the load_default_options
|
||||
# method.
|
||||
#
|
||||
# This method is part of the implementation of the load/dump interface of
|
||||
# Marshal and YAML.
|
||||
def load(source, proc = nil, options = {})
|
||||
opts = load_default_options.merge options
|
||||
if source.respond_to? :to_str
|
||||
source = source.to_str
|
||||
elsif source.respond_to? :to_io
|
||||
source = source.to_io.read
|
||||
elsif source.respond_to?(:read)
|
||||
source = source.read
|
||||
end
|
||||
if opts[:quirks_mode] && (source.nil? || source.empty?)
|
||||
source = 'null'
|
||||
end
|
||||
result = parse(source, opts)
|
||||
recurse_proc(result, &proc) if proc
|
||||
result
|
||||
end
|
||||
|
||||
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
||||
def recurse_proc(result, &proc)
|
||||
case result
|
||||
when Array
|
||||
result.each { |x| recurse_proc x, &proc }
|
||||
proc.call result
|
||||
when Hash
|
||||
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
|
||||
proc.call result
|
||||
else
|
||||
proc.call result
|
||||
end
|
||||
end
|
||||
|
||||
alias restore load
|
||||
module_function :restore
|
||||
|
||||
class << self
|
||||
# The global default options for the JSON.dump method:
|
||||
# :max_nesting: false
|
||||
# :allow_nan: true
|
||||
# :quirks_mode: true
|
||||
attr_accessor :dump_default_options
|
||||
end
|
||||
self.dump_default_options = {
|
||||
:max_nesting => false,
|
||||
:allow_nan => true,
|
||||
:quirks_mode => true,
|
||||
}
|
||||
|
||||
# Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
|
||||
# the result.
|
||||
#
|
||||
# If anIO (an IO-like object or an object that responds to the write method)
|
||||
# was given, the resulting JSON is written to it.
|
||||
#
|
||||
# If the number of nested arrays or objects exceeds _limit_, an ArgumentError
|
||||
# exception is raised. This argument is similar (but not exactly the
|
||||
# same!) to the _limit_ argument in Marshal.dump.
|
||||
#
|
||||
# The default options for the generator can be changed via the
|
||||
# dump_default_options method.
|
||||
#
|
||||
# This method is part of the implementation of the load/dump interface of
|
||||
# Marshal and YAML.
|
||||
def dump(obj, anIO = nil, limit = nil)
|
||||
if anIO and limit.nil?
|
||||
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|
||||
unless anIO.respond_to?(:write)
|
||||
limit = anIO
|
||||
anIO = nil
|
||||
end
|
||||
end
|
||||
opts = JSON.dump_default_options
|
||||
opts = opts.merge(:max_nesting => limit) if limit
|
||||
result = generate(obj, opts)
|
||||
if anIO
|
||||
anIO.write result
|
||||
anIO
|
||||
else
|
||||
result
|
||||
end
|
||||
rescue JSON::NestingError
|
||||
raise ArgumentError, "exceed depth limit"
|
||||
end
|
||||
|
||||
# Swap consecutive bytes of _string_ in place.
|
||||
def self.swap!(string) # :nodoc:
|
||||
0.upto(string.size / 2) do |i|
|
||||
break unless string[2 * i + 1]
|
||||
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
|
||||
end
|
||||
string
|
||||
end
|
||||
|
||||
# Shortcut for iconv.
|
||||
if ::String.method_defined?(:encode)
|
||||
# Encodes string using Ruby's _String.encode_
|
||||
def self.iconv(to, from, string)
|
||||
string.encode(to, from)
|
||||
end
|
||||
else
|
||||
require 'iconv'
|
||||
# Encodes string using _iconv_ library
|
||||
def self.iconv(to, from, string)
|
||||
Iconv.conv(to, from, string)
|
||||
end
|
||||
end
|
||||
|
||||
if ::Object.method(:const_defined?).arity == 1
|
||||
def self.const_defined_in?(modul, constant)
|
||||
modul.const_defined?(constant)
|
||||
end
|
||||
else
|
||||
def self.const_defined_in?(modul, constant)
|
||||
modul.const_defined?(constant, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ::Kernel
|
||||
private
|
||||
|
||||
# Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
|
||||
# one line.
|
||||
def j(*objs)
|
||||
objs.each do |obj|
|
||||
puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
|
||||
# indentation and over many lines.
|
||||
def jj(*objs)
|
||||
objs.each do |obj|
|
||||
puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# 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.
|
||||
def JSON(object, *args)
|
||||
if object.respond_to? :to_str
|
||||
JSON.parse(object.to_str, args.first)
|
||||
else
|
||||
JSON.generate(object, args.first)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Extends any Class to include _json_creatable?_ method.
|
||||
class ::Class
|
||||
# Returns true if this class can be used to create an instance
|
||||
# from a serialised JSON string. The class has to implement a class
|
||||
# method _json_create_ that expects a hash as first parameter. The hash
|
||||
# should include the required data.
|
||||
def json_creatable?
|
||||
respond_to?(:json_create)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
if ENV['SIMPLECOV_COVERAGE'].to_i == 1
|
||||
require 'simplecov'
|
||||
SimpleCov.start do
|
||||
add_filter "/tests/"
|
||||
end
|
||||
end
|
||||
require 'json/common'
|
||||
|
||||
module JSON
|
||||
# This module holds all the modules/classes that implement JSON's
|
||||
# functionality as C extensions.
|
||||
module Ext
|
||||
require 'json/ext/parser'
|
||||
require 'json/ext/generator'
|
||||
$DEBUG and warn "Using Ext extension for JSON."
|
||||
JSON.parser = Parser
|
||||
JSON.generator = Generator
|
||||
end
|
||||
|
||||
JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
require 'ostruct'
|
||||
|
||||
module JSON
|
||||
class GenericObject < OpenStruct
|
||||
class << self
|
||||
alias [] new
|
||||
|
||||
def json_creatable?
|
||||
@json_creatable
|
||||
end
|
||||
|
||||
attr_writer :json_creatable
|
||||
|
||||
def json_create(data)
|
||||
data = data.dup
|
||||
data.delete JSON.create_id
|
||||
self[data]
|
||||
end
|
||||
|
||||
def from_hash(object)
|
||||
case
|
||||
when object.respond_to?(:to_hash)
|
||||
result = new
|
||||
object.to_hash.each do |key, value|
|
||||
result[key] = from_hash(value)
|
||||
end
|
||||
result
|
||||
when object.respond_to?(:to_ary)
|
||||
object.to_ary.map { |a| from_hash(a) }
|
||||
else
|
||||
object
|
||||
end
|
||||
end
|
||||
|
||||
def load(source, proc = nil, opts = {})
|
||||
result = ::JSON.load(source, proc, opts.merge(:object_class => self))
|
||||
result.nil? ? new : result
|
||||
end
|
||||
|
||||
def dump(obj, *args)
|
||||
::JSON.dump(obj, *args)
|
||||
end
|
||||
end
|
||||
self.json_creatable = false
|
||||
|
||||
def to_hash
|
||||
table
|
||||
end
|
||||
|
||||
def |(other)
|
||||
self.class[other.to_hash.merge(to_hash)]
|
||||
end
|
||||
|
||||
def as_json(*)
|
||||
{ JSON.create_id => self.class.name }.merge to_hash
|
||||
end
|
||||
|
||||
def to_json(*a)
|
||||
as_json.to_json(*a)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
module JSON
|
||||
# JSON version
|
||||
VERSION = '1.8.1'
|
||||
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
|
||||
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
||||
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
||||
VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
$(OBJS): $(ruby_headers)
|
||||
parser.o: parser.c parser.h $(srcdir)/../fbuffer/fbuffer.h
|
||||
|
||||
# AUTOGENERATED DEPENDENCIES START
|
||||
parser.o: $(RUBY_EXTCONF_H)
|
||||
parser.o: $(arch_hdrdir)/ruby/config.h
|
||||
parser.o: $(hdrdir)/ruby/defines.h
|
||||
parser.o: $(hdrdir)/ruby/encoding.h
|
||||
parser.o: $(hdrdir)/ruby/intern.h
|
||||
parser.o: $(hdrdir)/ruby/missing.h
|
||||
parser.o: $(hdrdir)/ruby/oniguruma.h
|
||||
parser.o: $(hdrdir)/ruby/ruby.h
|
||||
parser.o: $(hdrdir)/ruby/st.h
|
||||
parser.o: $(hdrdir)/ruby/subst.h
|
||||
parser.o: $(top_srcdir)/ext/json/fbuffer/fbuffer.h
|
||||
parser.o: $(top_srcdir)/include/ruby.h
|
||||
parser.o: parser.c
|
||||
parser.o: parser.h
|
||||
parser.o: parser.rl
|
||||
# AUTOGENERATED DEPENDENCIES END
|
|
@ -0,0 +1,3 @@
|
|||
require 'mkmf'
|
||||
|
||||
create_makefile 'json/ext/parser'
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,93 @@
|
|||
#ifndef _PARSER_H_
|
||||
#define _PARSER_H_
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
#ifndef HAVE_RUBY_RE_H
|
||||
#include "re.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RUBY_ST_H
|
||||
#include "ruby/st.h"
|
||||
#else
|
||||
#include "st.h"
|
||||
#endif
|
||||
|
||||
#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
|
||||
|
||||
/* unicode */
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
typedef struct JSON_ParserStruct {
|
||||
VALUE Vsource;
|
||||
char *source;
|
||||
long len;
|
||||
char *memo;
|
||||
VALUE create_id;
|
||||
int max_nesting;
|
||||
int current_nesting;
|
||||
int allow_nan;
|
||||
int parsing_name;
|
||||
int symbolize_names;
|
||||
int quirks_mode;
|
||||
VALUE object_class;
|
||||
VALUE array_class;
|
||||
int create_additions;
|
||||
VALUE match_string;
|
||||
FBuffer *fbuffer;
|
||||
} JSON_Parser;
|
||||
|
||||
#define GET_PARSER \
|
||||
GET_PARSER_INIT; \
|
||||
if (!json->Vsource) rb_raise(rb_eTypeError, "uninitialized instance")
|
||||
#define GET_PARSER_INIT \
|
||||
JSON_Parser *json; \
|
||||
TypedData_Get_Struct(self, JSON_Parser, &JSON_Parser_type, json)
|
||||
|
||||
#define MinusInfinity "-Infinity"
|
||||
#define EVIL 0x666
|
||||
|
||||
static UTF32 unescape_unicode(const unsigned char *p);
|
||||
static int convert_UTF32_to_UTF8(char *buf, UTF32 ch);
|
||||
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd);
|
||||
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
|
||||
static VALUE convert_encoding(VALUE source);
|
||||
static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self);
|
||||
static VALUE cParser_parse(VALUE self);
|
||||
static JSON_Parser *JSON_allocate(void);
|
||||
static void JSON_mark(void *json);
|
||||
static void JSON_free(void *json);
|
||||
static VALUE cJSON_parser_s_allocate(VALUE klass);
|
||||
static VALUE cParser_source(VALUE self);
|
||||
#ifndef ZALLOC
|
||||
#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type)))
|
||||
static inline void *ruby_zalloc(size_t n)
|
||||
{
|
||||
void *p = ruby_xmalloc(n);
|
||||
memset(p, 0, n);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
#ifdef TypedData_Wrap_Struct
|
||||
static const rb_data_type_t JSON_Parser_type;
|
||||
#define NEW_TYPEDDATA_WRAPPER 1
|
||||
#else
|
||||
#define TypedData_Wrap_Struct(klass, ignore, json) Data_Wrap_Struct(klass, JSON_mark, JSON_free, json)
|
||||
#define TypedData_Get_Struct(self, JSON_Parser, ignore, json) Data_Get_Struct(self, JSON_Parser, json)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,945 @@
|
|||
#include "../fbuffer/fbuffer.h"
|
||||
#include "parser.h"
|
||||
|
||||
/* unicode */
|
||||
|
||||
static const char digit_values[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
|
||||
-1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
static UTF32 unescape_unicode(const unsigned char *p)
|
||||
{
|
||||
char b;
|
||||
UTF32 result = 0;
|
||||
b = digit_values[p[0]];
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
result = (result << 4) | (unsigned char)b;
|
||||
b = digit_values[p[1]];
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
result = (result << 4) | (unsigned char)b;
|
||||
b = digit_values[p[2]];
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
result = (result << 4) | (unsigned char)b;
|
||||
b = digit_values[p[3]];
|
||||
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
||||
result = (result << 4) | (unsigned char)b;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
|
||||
{
|
||||
int len = 1;
|
||||
if (ch <= 0x7F) {
|
||||
buf[0] = (char) ch;
|
||||
} else if (ch <= 0x07FF) {
|
||||
buf[0] = (char) ((ch >> 6) | 0xC0);
|
||||
buf[1] = (char) ((ch & 0x3F) | 0x80);
|
||||
len++;
|
||||
} else if (ch <= 0xFFFF) {
|
||||
buf[0] = (char) ((ch >> 12) | 0xE0);
|
||||
buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
|
||||
buf[2] = (char) ((ch & 0x3F) | 0x80);
|
||||
len += 2;
|
||||
} else if (ch <= 0x1fffff) {
|
||||
buf[0] =(char) ((ch >> 18) | 0xF0);
|
||||
buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
|
||||
buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
|
||||
buf[3] =(char) ((ch & 0x3F) | 0x80);
|
||||
len += 3;
|
||||
} else {
|
||||
buf[0] = '?';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#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;
|
||||
#else
|
||||
static ID i_iconv;
|
||||
#endif
|
||||
|
||||
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_object_class, i_array_class, i_key_p, i_deep_const_get, i_match,
|
||||
i_match_string, i_aset, i_aref, i_leftshift;
|
||||
|
||||
%%{
|
||||
machine JSON_common;
|
||||
|
||||
cr = '\n';
|
||||
cr_neg = [^\n];
|
||||
ws = [ \t\r\n];
|
||||
c_comment = '/*' ( any* - (any* '*/' any* ) ) '*/';
|
||||
cpp_comment = '//' cr_neg* cr;
|
||||
comment = c_comment | cpp_comment;
|
||||
ignore = ws | comment;
|
||||
name_separator = ':';
|
||||
value_separator = ',';
|
||||
Vnull = 'null';
|
||||
Vfalse = 'false';
|
||||
Vtrue = 'true';
|
||||
VNaN = 'NaN';
|
||||
VInfinity = 'Infinity';
|
||||
VMinusInfinity = '-Infinity';
|
||||
begin_value = [nft\"\-\[\{NI] | digit;
|
||||
begin_object = '{';
|
||||
end_object = '}';
|
||||
begin_array = '[';
|
||||
end_array = ']';
|
||||
begin_string = '"';
|
||||
begin_name = begin_string;
|
||||
begin_number = digit | '-';
|
||||
}%%
|
||||
|
||||
%%{
|
||||
machine JSON_object;
|
||||
include JSON_common;
|
||||
|
||||
write data;
|
||||
|
||||
action parse_value {
|
||||
VALUE v = Qnil;
|
||||
char *np = JSON_parse_value(json, fpc, pe, &v);
|
||||
if (np == NULL) {
|
||||
fhold; fbreak;
|
||||
} else {
|
||||
if (NIL_P(json->object_class)) {
|
||||
rb_hash_aset(*result, last_name, v);
|
||||
} else {
|
||||
rb_funcall(*result, i_aset, 2, last_name, v);
|
||||
}
|
||||
fexec np;
|
||||
}
|
||||
}
|
||||
|
||||
action parse_name {
|
||||
char *np;
|
||||
json->parsing_name = 1;
|
||||
np = JSON_parse_string(json, fpc, pe, &last_name);
|
||||
json->parsing_name = 0;
|
||||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
action exit { fhold; fbreak; }
|
||||
|
||||
pair = ignore* begin_name >parse_name ignore* name_separator ignore* begin_value >parse_value;
|
||||
next_pair = ignore* value_separator pair;
|
||||
|
||||
main := (
|
||||
begin_object
|
||||
(pair (next_pair)*)? ignore*
|
||||
end_object
|
||||
) @exit;
|
||||
}%%
|
||||
|
||||
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
VALUE last_name = Qnil;
|
||||
VALUE object_class = json->object_class;
|
||||
|
||||
if (json->max_nesting && json->current_nesting > json->max_nesting) {
|
||||
rb_raise(eNestingError, "nesting of %d is too deep", json->current_nesting);
|
||||
}
|
||||
|
||||
*result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
|
||||
|
||||
%% write init;
|
||||
%% write exec;
|
||||
|
||||
if (cs >= JSON_object_first_final) {
|
||||
if (json->create_additions) {
|
||||
VALUE klassname;
|
||||
if (NIL_P(json->object_class)) {
|
||||
klassname = rb_hash_aref(*result, json->create_id);
|
||||
} else {
|
||||
klassname = rb_funcall(*result, i_aref, 1, json->create_id);
|
||||
}
|
||||
if (!NIL_P(klassname)) {
|
||||
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
|
||||
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
|
||||
*result = rb_funcall(klass, i_json_create, 1, *result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return p + 1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
%%{
|
||||
machine JSON_value;
|
||||
include JSON_common;
|
||||
|
||||
write data;
|
||||
|
||||
action parse_null {
|
||||
*result = Qnil;
|
||||
}
|
||||
action parse_false {
|
||||
*result = Qfalse;
|
||||
}
|
||||
action parse_true {
|
||||
*result = Qtrue;
|
||||
}
|
||||
action parse_nan {
|
||||
if (json->allow_nan) {
|
||||
*result = CNaN;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
|
||||
}
|
||||
}
|
||||
action parse_infinity {
|
||||
if (json->allow_nan) {
|
||||
*result = CInfinity;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
|
||||
}
|
||||
}
|
||||
action parse_string {
|
||||
char *np = JSON_parse_string(json, fpc, pe, result);
|
||||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
action parse_number {
|
||||
char *np;
|
||||
if(pe > fpc + 9 - json->quirks_mode && !strncmp(MinusInfinity, fpc, 9)) {
|
||||
if (json->allow_nan) {
|
||||
*result = CMinusInfinity;
|
||||
fexec p + 10;
|
||||
fhold; fbreak;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
|
||||
}
|
||||
}
|
||||
np = JSON_parse_float(json, fpc, pe, result);
|
||||
if (np != NULL) fexec np;
|
||||
np = JSON_parse_integer(json, fpc, pe, result);
|
||||
if (np != NULL) fexec np;
|
||||
fhold; fbreak;
|
||||
}
|
||||
|
||||
action parse_array {
|
||||
char *np;
|
||||
json->current_nesting++;
|
||||
np = JSON_parse_array(json, fpc, pe, result);
|
||||
json->current_nesting--;
|
||||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
action parse_object {
|
||||
char *np;
|
||||
json->current_nesting++;
|
||||
np = JSON_parse_object(json, fpc, pe, result);
|
||||
json->current_nesting--;
|
||||
if (np == NULL) { fhold; fbreak; } else fexec np;
|
||||
}
|
||||
|
||||
action exit { fhold; fbreak; }
|
||||
|
||||
main := (
|
||||
Vnull @parse_null |
|
||||
Vfalse @parse_false |
|
||||
Vtrue @parse_true |
|
||||
VNaN @parse_nan |
|
||||
VInfinity @parse_infinity |
|
||||
begin_number >parse_number |
|
||||
begin_string >parse_string |
|
||||
begin_array >parse_array |
|
||||
begin_object >parse_object
|
||||
) %*exit;
|
||||
}%%
|
||||
|
||||
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
|
||||
%% write init;
|
||||
%% write exec;
|
||||
|
||||
if (cs >= JSON_value_first_final) {
|
||||
return p;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
%%{
|
||||
machine JSON_integer;
|
||||
|
||||
write data;
|
||||
|
||||
action exit { fhold; fbreak; }
|
||||
|
||||
main := '-'? ('0' | [1-9][0-9]*) (^[0-9]? @exit);
|
||||
}%%
|
||||
|
||||
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
|
||||
%% write init;
|
||||
json->memo = p;
|
||||
%% write exec;
|
||||
|
||||
if (cs >= JSON_integer_first_final) {
|
||||
long len = p - json->memo;
|
||||
fbuffer_clear(json->fbuffer);
|
||||
fbuffer_append(json->fbuffer, json->memo, len);
|
||||
fbuffer_append_char(json->fbuffer, '\0');
|
||||
*result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10);
|
||||
return p + 1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
%%{
|
||||
machine JSON_float;
|
||||
include JSON_common;
|
||||
|
||||
write data;
|
||||
|
||||
action exit { fhold; fbreak; }
|
||||
|
||||
main := '-'? (
|
||||
(('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?)
|
||||
| (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+))
|
||||
) (^[0-9Ee.\-]? @exit );
|
||||
}%%
|
||||
|
||||
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
|
||||
%% write init;
|
||||
json->memo = p;
|
||||
%% write exec;
|
||||
|
||||
if (cs >= JSON_float_first_final) {
|
||||
long len = p - json->memo;
|
||||
fbuffer_clear(json->fbuffer);
|
||||
fbuffer_append(json->fbuffer, json->memo, len);
|
||||
fbuffer_append_char(json->fbuffer, '\0');
|
||||
*result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
|
||||
return p + 1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
%%{
|
||||
machine JSON_array;
|
||||
include JSON_common;
|
||||
|
||||
write data;
|
||||
|
||||
action parse_value {
|
||||
VALUE v = Qnil;
|
||||
char *np = JSON_parse_value(json, fpc, pe, &v);
|
||||
if (np == NULL) {
|
||||
fhold; fbreak;
|
||||
} else {
|
||||
if (NIL_P(json->array_class)) {
|
||||
rb_ary_push(*result, v);
|
||||
} else {
|
||||
rb_funcall(*result, i_leftshift, 1, v);
|
||||
}
|
||||
fexec np;
|
||||
}
|
||||
}
|
||||
|
||||
action exit { fhold; fbreak; }
|
||||
|
||||
next_element = value_separator ignore* begin_value >parse_value;
|
||||
|
||||
main := begin_array ignore*
|
||||
((begin_value >parse_value ignore*)
|
||||
(ignore* next_element ignore*)*)?
|
||||
end_array @exit;
|
||||
}%%
|
||||
|
||||
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
VALUE array_class = json->array_class;
|
||||
|
||||
if (json->max_nesting && json->current_nesting > json->max_nesting) {
|
||||
rb_raise(eNestingError, "nesting of %d is too deep", json->current_nesting);
|
||||
}
|
||||
*result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
|
||||
|
||||
%% write init;
|
||||
%% write exec;
|
||||
|
||||
if(cs >= JSON_array_first_final) {
|
||||
return p + 1;
|
||||
} else {
|
||||
rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
|
||||
{
|
||||
char *p = string, *pe = string, *unescape;
|
||||
int unescape_len;
|
||||
char buf[4];
|
||||
|
||||
while (pe < stringEnd) {
|
||||
if (*pe == '\\') {
|
||||
unescape = (char *) "?";
|
||||
unescape_len = 1;
|
||||
if (pe > p) rb_str_buf_cat(result, p, pe - p);
|
||||
switch (*++pe) {
|
||||
case 'n':
|
||||
unescape = (char *) "\n";
|
||||
break;
|
||||
case 'r':
|
||||
unescape = (char *) "\r";
|
||||
break;
|
||||
case 't':
|
||||
unescape = (char *) "\t";
|
||||
break;
|
||||
case '"':
|
||||
unescape = (char *) "\"";
|
||||
break;
|
||||
case '\\':
|
||||
unescape = (char *) "\\";
|
||||
break;
|
||||
case 'b':
|
||||
unescape = (char *) "\b";
|
||||
break;
|
||||
case 'f':
|
||||
unescape = (char *) "\f";
|
||||
break;
|
||||
case 'u':
|
||||
if (pe > stringEnd - 4) {
|
||||
return Qnil;
|
||||
} else {
|
||||
UTF32 ch = unescape_unicode((unsigned char *) ++pe);
|
||||
pe += 3;
|
||||
if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
|
||||
pe++;
|
||||
if (pe > stringEnd - 6) return Qnil;
|
||||
if (pe[0] == '\\' && pe[1] == 'u') {
|
||||
UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
|
||||
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
||||
| (sur & 0x3FF));
|
||||
pe += 5;
|
||||
} else {
|
||||
unescape = (char *) "?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
unescape_len = convert_UTF32_to_UTF8(buf, ch);
|
||||
unescape = buf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p = pe;
|
||||
continue;
|
||||
}
|
||||
rb_str_buf_cat(result, unescape, unescape_len);
|
||||
p = ++pe;
|
||||
} else {
|
||||
pe++;
|
||||
}
|
||||
}
|
||||
rb_str_buf_cat(result, p, pe - p);
|
||||
return result;
|
||||
}
|
||||
|
||||
%%{
|
||||
machine JSON_string;
|
||||
include JSON_common;
|
||||
|
||||
write data;
|
||||
|
||||
action parse_string {
|
||||
*result = json_string_unescape(*result, json->memo + 1, p);
|
||||
if (NIL_P(*result)) {
|
||||
fhold;
|
||||
fbreak;
|
||||
} else {
|
||||
FORCE_UTF8(*result);
|
||||
fexec p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
action exit { fhold; fbreak; }
|
||||
|
||||
main := '"' ((^([\"\\] | 0..0x1f) | '\\'[\"\\/bfnrt] | '\\u'[0-9a-fA-F]{4} | '\\'^([\"\\/bfnrtu]|0..0x1f))* %parse_string) '"' @exit;
|
||||
}%%
|
||||
|
||||
static int
|
||||
match_i(VALUE regexp, VALUE klass, VALUE memo)
|
||||
{
|
||||
if (regexp == Qundef) return ST_STOP;
|
||||
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
|
||||
RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
|
||||
rb_ary_push(memo, klass);
|
||||
return ST_STOP;
|
||||
}
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
|
||||
{
|
||||
int cs = EVIL;
|
||||
VALUE match_string;
|
||||
|
||||
*result = rb_str_buf_new(0);
|
||||
%% write init;
|
||||
json->memo = p;
|
||||
%% write exec;
|
||||
|
||||
if (json->create_additions && RTEST(match_string = json->match_string)) {
|
||||
VALUE klass;
|
||||
VALUE memo = rb_ary_new2(2);
|
||||
rb_ary_push(memo, *result);
|
||||
rb_hash_foreach(match_string, match_i, memo);
|
||||
klass = rb_ary_entry(memo, 1);
|
||||
if (RTEST(klass)) {
|
||||
*result = rb_funcall(klass, i_json_create, 1, *result);
|
||||
}
|
||||
}
|
||||
|
||||
if (json->symbolize_names && json->parsing_name) {
|
||||
*result = rb_str_intern(*result);
|
||||
}
|
||||
if (cs >= JSON_string_first_final) {
|
||||
return p + 1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-class: JSON::Ext::Parser
|
||||
*
|
||||
* This is the JSON parser implemented as a C extension. It can be configured
|
||||
* to be used by setting
|
||||
*
|
||||
* JSON.parser = JSON::Ext::Parser
|
||||
*
|
||||
* with the method parser= in JSON.
|
||||
*
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
return source;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: new(source, opts => {})
|
||||
*
|
||||
* Creates a new JSON::Ext::Parser instance for the string _source_.
|
||||
*
|
||||
* Creates a new JSON::Ext::Parser instance for the string _source_.
|
||||
*
|
||||
* It will be configured by the _opts_ hash. _opts_ can have the following
|
||||
* keys:
|
||||
*
|
||||
* _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|nil|0, 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.
|
||||
* * *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.
|
||||
* * *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.
|
||||
* * *object_class*: Defaults to Hash
|
||||
* * *array_class*: Defaults to Array
|
||||
*/
|
||||
static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE source, opts;
|
||||
GET_PARSER_INIT;
|
||||
|
||||
if (json->Vsource) {
|
||||
rb_raise(rb_eTypeError, "already initialized instance");
|
||||
}
|
||||
rb_scan_args(argc, argv, "11", &source, &opts);
|
||||
if (!NIL_P(opts)) {
|
||||
opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
|
||||
if (NIL_P(opts)) {
|
||||
rb_raise(rb_eArgError, "opts needs to be like a hash");
|
||||
} else {
|
||||
VALUE tmp = ID2SYM(i_max_nesting);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE max_nesting = rb_hash_aref(opts, tmp);
|
||||
if (RTEST(max_nesting)) {
|
||||
Check_Type(max_nesting, T_FIXNUM);
|
||||
json->max_nesting = FIX2INT(max_nesting);
|
||||
} else {
|
||||
json->max_nesting = 0;
|
||||
}
|
||||
} else {
|
||||
json->max_nesting = 100;
|
||||
}
|
||||
tmp = ID2SYM(i_allow_nan);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
|
||||
} else {
|
||||
json->allow_nan = 0;
|
||||
}
|
||||
tmp = ID2SYM(i_symbolize_names);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
|
||||
} 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;
|
||||
}
|
||||
tmp = ID2SYM(i_create_id);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->create_id = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
||||
}
|
||||
tmp = ID2SYM(i_object_class);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->object_class = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->object_class = Qnil;
|
||||
}
|
||||
tmp = ID2SYM(i_array_class);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
json->array_class = rb_hash_aref(opts, tmp);
|
||||
} else {
|
||||
json->array_class = Qnil;
|
||||
}
|
||||
tmp = ID2SYM(i_match_string);
|
||||
if (option_given_p(opts, tmp)) {
|
||||
VALUE match_string = rb_hash_aref(opts, tmp);
|
||||
json->match_string = RTEST(match_string) ? match_string : Qnil;
|
||||
} else {
|
||||
json->match_string = Qnil;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
json->max_nesting = 100;
|
||||
json->allow_nan = 0;
|
||||
json->create_additions = 1;
|
||||
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
||||
json->object_class = Qnil;
|
||||
json->array_class = Qnil;
|
||||
}
|
||||
source = rb_convert_type(source, T_STRING, "String", "to_str");
|
||||
if (!json->quirks_mode) {
|
||||
source = convert_encoding(StringValue(source));
|
||||
}
|
||||
json->current_nesting = 0;
|
||||
StringValue(source);
|
||||
json->len = RSTRING_LEN(source);
|
||||
json->source = RSTRING_PTR(source);;
|
||||
json->Vsource = source;
|
||||
return self;
|
||||
}
|
||||
|
||||
%%{
|
||||
machine JSON;
|
||||
|
||||
write data;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
main := ignore* (
|
||||
begin_value >parse_value
|
||||
) 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()
|
||||
*
|
||||
* 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);
|
||||
} else {
|
||||
return cParser_parse_strict(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static JSON_Parser *JSON_allocate(void)
|
||||
{
|
||||
JSON_Parser *json = ZALLOC(JSON_Parser);
|
||||
json->fbuffer = fbuffer_alloc(0);
|
||||
return json;
|
||||
}
|
||||
|
||||
static void JSON_mark(void *ptr)
|
||||
{
|
||||
JSON_Parser *json = ptr;
|
||||
rb_gc_mark_maybe(json->Vsource);
|
||||
rb_gc_mark_maybe(json->create_id);
|
||||
rb_gc_mark_maybe(json->object_class);
|
||||
rb_gc_mark_maybe(json->array_class);
|
||||
rb_gc_mark_maybe(json->match_string);
|
||||
}
|
||||
|
||||
static void JSON_free(void *ptr)
|
||||
{
|
||||
JSON_Parser *json = ptr;
|
||||
fbuffer_free(json->fbuffer);
|
||||
ruby_xfree(json);
|
||||
}
|
||||
|
||||
static size_t JSON_memsize(const void *ptr)
|
||||
{
|
||||
const JSON_Parser *json = ptr;
|
||||
return sizeof(*json) + FBUFFER_CAPA(json->fbuffer);
|
||||
}
|
||||
|
||||
#ifdef NEW_TYPEDDATA_WRAPPER
|
||||
static const rb_data_type_t JSON_Parser_type = {
|
||||
"JSON/Parser",
|
||||
{JSON_mark, JSON_free, JSON_memsize,},
|
||||
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
||||
0, 0,
|
||||
RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
static VALUE cJSON_parser_s_allocate(VALUE klass)
|
||||
{
|
||||
JSON_Parser *json = JSON_allocate();
|
||||
return TypedData_Wrap_Struct(klass, &JSON_Parser_type, json);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: source()
|
||||
*
|
||||
* Returns a copy of the current _source_ string, that was used to construct
|
||||
* this Parser.
|
||||
*/
|
||||
static VALUE cParser_source(VALUE self)
|
||||
{
|
||||
GET_PARSER;
|
||||
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");
|
||||
mJSON = rb_define_module("JSON");
|
||||
mExt = rb_define_module_under(mJSON, "Ext");
|
||||
cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
|
||||
eParserError = rb_path2class("JSON::ParserError");
|
||||
eNestingError = rb_path2class("JSON::NestingError");
|
||||
rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
|
||||
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"));
|
||||
CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
|
||||
|
||||
i_json_creatable_p = rb_intern("json_creatable?");
|
||||
i_json_create = rb_intern("json_create");
|
||||
i_create_id = rb_intern("create_id");
|
||||
i_create_additions = rb_intern("create_additions");
|
||||
i_chr = rb_intern("chr");
|
||||
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");
|
||||
i_match_string = rb_intern("match_string");
|
||||
i_key_p = rb_intern("key?");
|
||||
i_deep_const_get = rb_intern("deep_const_get");
|
||||
i_aset = rb_intern("[]=");
|
||||
i_aref = rb_intern("[]");
|
||||
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");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: c
|
||||
* c-file-style: ruby
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
|
@ -0,0 +1,10 @@
|
|||
RAGEL = ragel
|
||||
|
||||
.SUFFIXES: .rl
|
||||
|
||||
.rl.c:
|
||||
$(RAGEL) -G2 $<
|
||||
$(BASERUBY) -pli -e '$$_.sub!(/[ \t]+$$/, "")' \
|
||||
-e '$$_.sub!(/^static const int (JSON_.*=.*);$$/, "enum {\\1};")' $@
|
||||
|
||||
parser.c:
|
|
@ -2,4 +2,3 @@ power_assert 0.2.2
|
|||
test-unit 3.0.9
|
||||
minitest 5.4.3
|
||||
rake 10.4.2
|
||||
json 1.8.2
|
||||
|
|
|
@ -3,11 +3,6 @@ require 'rubygems/user_interaction'
|
|||
require 'fileutils'
|
||||
require 'rdoc'
|
||||
|
||||
begin
|
||||
require 'json'
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
##
|
||||
# Gem::RDoc provides methods to generate RDoc and ri data for installed gems
|
||||
# upon gem installation.
|
||||
|
@ -205,7 +200,7 @@ class RDoc::RubygemsHook
|
|||
@generate_ri and (@force or not File.exist? @ri_dir)
|
||||
|
||||
document 'darkfish', options, @rdoc_dir if
|
||||
defined?(JSON) and @generate_rdoc and (@force or not File.exist? @rdoc_dir)
|
||||
@generate_rdoc and (@force or not File.exist? @rdoc_dir)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -255,3 +250,4 @@ class RDoc::RubygemsHook
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -6,11 +6,6 @@ rescue NoMethodError, Gem::LoadError
|
|||
# for ruby tests
|
||||
end
|
||||
|
||||
begin
|
||||
require 'json'
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/benchmark' if ENV['BENCHMARK']
|
||||
|
||||
|
@ -219,3 +214,4 @@ $LOAD_PATH.each do |load_path|
|
|||
break if load_path[0] == ?/
|
||||
load_path.replace File.expand_path load_path
|
||||
end if RUBY_VERSION < '1.9'
|
||||
|
||||
|
|
|
@ -21,16 +21,7 @@ end
|
|||
# them while we're testing rubygems, and thus we can't actually load them.
|
||||
unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
|
||||
gem 'rdoc'
|
||||
begin
|
||||
gem 'json'
|
||||
rescue Gem::LoadError
|
||||
# for Ruby 2.3.0
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'json'
|
||||
rescue LoadError
|
||||
gem 'json'
|
||||
end
|
||||
|
||||
require 'minitest/autorun'
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"A JSON payload should be an object or array, not a string."
|
|
@ -0,0 +1 @@
|
|||
{"Extra value after close": true} "misplaced quoted value"
|
|
@ -0,0 +1 @@
|
|||
{"Illegal expression": 1 + 2}
|
|
@ -0,0 +1 @@
|
|||
{"Illegal invocation": alert()}
|
|
@ -0,0 +1 @@
|
|||
{"Numbers cannot have leading zeroes": 013}
|
|
@ -0,0 +1 @@
|
|||
{"Numbers cannot be hex": 0x14}
|
|
@ -0,0 +1 @@
|
|||
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
|
|
@ -0,0 +1 @@
|
|||
{"Missing colon" null}
|
|
@ -0,0 +1 @@
|
|||
["Unclosed array"
|
|
@ -0,0 +1 @@
|
|||
{"Double colon":: null}
|
|
@ -0,0 +1 @@
|
|||
{"Comma instead of colon", null}
|
|
@ -0,0 +1 @@
|
|||
["Colon instead of comma": false]
|
|
@ -0,0 +1 @@
|
|||
["Bad value", truth]
|
|
@ -0,0 +1 @@
|
|||
['single quote']
|
|
@ -0,0 +1 @@
|
|||
["tab character in string "]
|
|
@ -0,0 +1,2 @@
|
|||
["line
|
||||
break"]
|
|
@ -0,0 +1,2 @@
|
|||
["line\
|
||||
break"]
|
|
@ -0,0 +1 @@
|
|||
{unquoted_key: "keys must be quoted"}
|
|
@ -0,0 +1 @@
|
|||
["extra comma",]
|
|
@ -0,0 +1 @@
|
|||
["double extra comma",,]
|
|
@ -0,0 +1 @@
|
|||
[ , "<-- missing value"]
|
|
@ -0,0 +1 @@
|
|||
["Comma after the close"],
|
|
@ -0,0 +1 @@
|
|||
["Extra close"]]
|
|
@ -0,0 +1 @@
|
|||
{"Extra comma": true,}
|
|
@ -0,0 +1,56 @@
|
|||
[
|
||||
"JSON Test Pattern pass1",
|
||||
{"object with 1 member":["array with 1 element"]},
|
||||
{},
|
||||
[],
|
||||
-42,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
{
|
||||
"integer": 1234567890,
|
||||
"real": -9876.543210,
|
||||
"e": 0.123456789e-12,
|
||||
"E": 1.234567890E+34,
|
||||
"": 23456789012E666,
|
||||
"zero": 0,
|
||||
"one": 1,
|
||||
"space": " ",
|
||||
"quote": "\"",
|
||||
"backslash": "\\",
|
||||
"controls": "\b\f\n\r\t",
|
||||
"slash": "/ & \/",
|
||||
"alpha": "abcdefghijklmnopqrstuvwyz",
|
||||
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
|
||||
"digit": "0123456789",
|
||||
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
|
||||
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
|
||||
"true": true,
|
||||
"false": false,
|
||||
"null": null,
|
||||
"array":[ ],
|
||||
"object":{ },
|
||||
"address": "50 St. James Street",
|
||||
"url": "http://www.JSON.org/",
|
||||
"comment": "// /* <!-- --",
|
||||
"# -- --> */": " ",
|
||||
" s p a c e d " :[1,2 , 3
|
||||
|
||||
,
|
||||
|
||||
4 , 5 , 6 ,7 ],
|
||||
"compact": [1,2,3,4,5,6,7],
|
||||
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
|
||||
"quotes": "" \u0022 %22 0x22 034 "",
|
||||
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
|
||||
: "A key can be any string"
|
||||
},
|
||||
0.5 ,98.6
|
||||
,
|
||||
99.44
|
||||
,
|
||||
|
||||
1066
|
||||
|
||||
|
||||
,"rosebud"]
|
|
@ -0,0 +1 @@
|
|||
["Illegal backslash escape: \x15"]
|
|
@ -0,0 +1 @@
|
|||
["Illegal backslash escape: \'"]
|
|
@ -0,0 +1 @@
|
|||
["Illegal backslash escape: \017"]
|
|
@ -0,0 +1 @@
|
|||
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
|
|
@ -0,0 +1 @@
|
|||
["tab\ character\ in\ string\ "]
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"JSON Test Pattern pass3": {
|
||||
"The outermost value": "must be an object or array.",
|
||||
"In this test": "It is an object."
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
case ENV['JSON']
|
||||
when 'pure'
|
||||
$:.unshift 'lib'
|
||||
require 'json/pure'
|
||||
when 'ext'
|
||||
$:.unshift 'ext', 'lib'
|
||||
require 'json/ext'
|
||||
else
|
||||
$:.unshift 'ext', 'lib'
|
||||
require 'json'
|
||||
end
|
|
@ -0,0 +1,553 @@
|
|||
#!/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
|
|
@ -0,0 +1,196 @@
|
|||
#!/usr/bin/env ruby
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
require 'test/unit'
|
||||
require File.join(File.dirname(__FILE__), 'setup_variant')
|
||||
require 'json/add/core'
|
||||
require 'json/add/complex'
|
||||
require 'json/add/rational'
|
||||
require 'json/add/bigdecimal'
|
||||
require 'json/add/ostruct'
|
||||
require 'date'
|
||||
|
||||
class TestJSONAddition < Test::Unit::TestCase
|
||||
include JSON
|
||||
|
||||
class A
|
||||
def initialize(a)
|
||||
@a = a
|
||||
end
|
||||
|
||||
attr_reader :a
|
||||
|
||||
def ==(other)
|
||||
a == other.a
|
||||
end
|
||||
|
||||
def self.json_create(object)
|
||||
new(*object['args'])
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
'args' => [ @a ],
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class A2 < A
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
'args' => [ @a ],
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class B
|
||||
def self.json_creatable?
|
||||
false
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => self.class.name,
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class C
|
||||
def self.json_creatable?
|
||||
false
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
'json_class' => 'TestJSONAddition::Nix',
|
||||
}.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def test_extended_json
|
||||
a = A.new(666)
|
||||
assert A.json_creatable?
|
||||
json = generate(a)
|
||||
a_again = JSON.parse(json, :create_additions => true)
|
||||
assert_kind_of a.class, a_again
|
||||
assert_equal a, a_again
|
||||
end
|
||||
|
||||
def test_extended_json_default
|
||||
a = A.new(666)
|
||||
assert A.json_creatable?
|
||||
json = generate(a)
|
||||
a_hash = JSON.parse(json)
|
||||
assert_kind_of Hash, a_hash
|
||||
end
|
||||
|
||||
def test_extended_json_disabled
|
||||
a = A.new(666)
|
||||
assert A.json_creatable?
|
||||
json = generate(a)
|
||||
a_again = JSON.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)
|
||||
assert_kind_of Hash, a_hash
|
||||
assert_equal(
|
||||
{"args"=>[666], "json_class"=>"TestJSONAddition::A"}.sort_by { |k,| k },
|
||||
a_hash.sort_by { |k,| k }
|
||||
)
|
||||
end
|
||||
|
||||
def test_extended_json_fail1
|
||||
b = B.new
|
||||
assert !B.json_creatable?
|
||||
json = generate(b)
|
||||
assert_equal({ "json_class"=>"TestJSONAddition::B" }, JSON.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) }
|
||||
end
|
||||
|
||||
def test_raw_strings
|
||||
raw = ''
|
||||
raw.respond_to?(:encode!) and raw.encode!(Encoding::ASCII_8BIT)
|
||||
raw_array = []
|
||||
for i in 0..255
|
||||
raw << i
|
||||
raw_array << i
|
||||
end
|
||||
json = raw.to_json_raw
|
||||
json_raw_object = raw.to_json_raw_object
|
||||
hash = { 'json_class' => 'String', 'raw'=> raw_array }
|
||||
assert_equal hash, json_raw_object
|
||||
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)
|
||||
assert_equal raw, raw_again
|
||||
end
|
||||
|
||||
MyJsonStruct = Struct.new 'MyJsonStruct', :foo, :bar
|
||||
|
||||
def test_core
|
||||
t = Time.now
|
||||
assert_equal t, JSON(JSON(t), :create_additions => true)
|
||||
d = Date.today
|
||||
assert_equal d, JSON(JSON(d), :create_additions => true)
|
||||
d = DateTime.civil(2007, 6, 14, 14, 57, 10, Rational(1, 12), 2299161)
|
||||
assert_equal d, JSON(JSON(d), :create_additions => true)
|
||||
assert_equal 1..10, JSON(JSON(1..10), :create_additions => true)
|
||||
assert_equal 1...10, JSON(JSON(1...10), :create_additions => true)
|
||||
assert_equal "a".."c", JSON(JSON("a".."c"), :create_additions => true)
|
||||
assert_equal "a"..."c", JSON(JSON("a"..."c"), :create_additions => true)
|
||||
s = MyJsonStruct.new 4711, 'foot'
|
||||
assert_equal s, JSON(JSON(s), :create_additions => true)
|
||||
struct = Struct.new :foo, :bar
|
||||
s = struct.new 4711, 'foot'
|
||||
assert_raises(JSONError) { JSON(s) }
|
||||
begin
|
||||
raise TypeError, "test me"
|
||||
rescue TypeError => e
|
||||
e_json = JSON.generate e
|
||||
e_again = JSON e_json, :create_additions => true
|
||||
assert_kind_of TypeError, e_again
|
||||
assert_equal e.message, e_again.message
|
||||
assert_equal e.backtrace, e_again.backtrace
|
||||
end
|
||||
assert_equal(/foo/, JSON(JSON(/foo/), :create_additions => true))
|
||||
assert_equal(/foo/i, JSON(JSON(/foo/i), :create_additions => true))
|
||||
end
|
||||
|
||||
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.civil(2008, 6, 17, 11, 48, 32, Rational(1,24))
|
||||
assert_equal d, JSON.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)
|
||||
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)
|
||||
end
|
||||
|
||||
def test_bigdecimal
|
||||
assert_equal BigDecimal('3.141', 23), JSON(JSON(BigDecimal('3.141', 23)), :create_additions => true)
|
||||
assert_equal BigDecimal('3.141', 666), JSON(JSON(BigDecimal('3.141', 666)), :create_additions => true)
|
||||
end
|
||||
|
||||
def test_ostruct
|
||||
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)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
#!/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
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env ruby
|
||||
# encoding: utf-8
|
||||
|
||||
require 'test/unit'
|
||||
require File.join(File.dirname(__FILE__), 'setup_variant')
|
||||
|
||||
class TestJSONFixtures < Test::Unit::TestCase
|
||||
def setup
|
||||
fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.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
|
||||
end
|
||||
|
||||
def test_passing
|
||||
for name, source in @passed
|
||||
begin
|
||||
assert JSON.parse(source),
|
||||
"Did not pass for fixture '#{name}': #{source.inspect}"
|
||||
rescue => e
|
||||
warn "\nCaught #{e.class}(#{e}) for fixture '#{name}': #{source.inspect}\n#{e.backtrace * "\n"}"
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failing
|
||||
for name, source in @failed
|
||||
assert_raises(JSON::ParserError, JSON::NestingError,
|
||||
"Did not fail for fixture '#{name}': #{source.inspect}") do
|
||||
JSON.parse(source)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,347 @@
|
|||
#!/usr/bin/env ruby
|
||||
# encoding: utf-8
|
||||
|
||||
require 'test/unit'
|
||||
require File.join(File.dirname(__FILE__), 'setup_variant')
|
||||
|
||||
class TestJSONGenerate < 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
|
||||
}
|
||||
@json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
|
||||
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
|
||||
@json3 = <<'EOT'.chomp
|
||||
{
|
||||
"a": 2,
|
||||
"b": 3.141,
|
||||
"c": "c",
|
||||
"d": [
|
||||
1,
|
||||
"b",
|
||||
3.14
|
||||
],
|
||||
"e": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"g": "\"\u0000\u001f",
|
||||
"h": 1000.0,
|
||||
"i": 0.001
|
||||
}
|
||||
EOT
|
||||
end
|
||||
|
||||
def test_generate
|
||||
json = generate(@hash)
|
||||
assert_equal(JSON.parse(@json2), JSON.parse(json))
|
||||
json = JSON[@hash]
|
||||
assert_equal(JSON.parse(@json2), JSON.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)
|
||||
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))
|
||||
parsed_json = parse(json)
|
||||
assert_equal(@hash, parsed_json)
|
||||
json = pretty_generate({1=>2})
|
||||
assert_equal(<<'EOT'.chomp, json)
|
||||
{
|
||||
"1": 2
|
||||
}
|
||||
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)
|
||||
end
|
||||
|
||||
def test_generate_custom
|
||||
state = State.new(:space_before => " ", :space => " ", :indent => "<i>", :object_nl => "\n", :array_nl => "<a_nl>")
|
||||
json = generate({1=>{2=>3,4=>[5,6]}}, state)
|
||||
assert_equal(<<'EOT'.chomp, json)
|
||||
{
|
||||
<i>"1" : {
|
||||
<i><i>"2" : 3,
|
||||
<i><i>"4" : [<a_nl><i><i><i>5,<a_nl><i><i><i>6<a_nl><i><i>]
|
||||
<i>}
|
||||
}
|
||||
EOT
|
||||
end
|
||||
|
||||
def test_fast_generate
|
||||
json = fast_generate(@hash)
|
||||
assert_equal(JSON.parse(@json2), JSON.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)
|
||||
end
|
||||
|
||||
def test_own_state
|
||||
state = State.new
|
||||
json = generate(@hash, state)
|
||||
assert_equal(JSON.parse(@json2), JSON.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
|
||||
|
||||
def test_states
|
||||
json = generate({1=>2}, nil)
|
||||
assert_equal('{"1":2}', json)
|
||||
s = JSON.state.new
|
||||
assert s.check_circular?
|
||||
assert s[:check_circular?]
|
||||
h = { 1=>2 }
|
||||
h[3] = h
|
||||
assert_raises(JSON::NestingError) { generate(h) }
|
||||
assert_raises(JSON::NestingError) { generate(h, s) }
|
||||
s = JSON.state.new
|
||||
a = [ 1, 2 ]
|
||||
a << a
|
||||
assert_raises(JSON::NestingError) { generate(a, s) }
|
||||
assert s.check_circular?
|
||||
assert s[:check_circular?]
|
||||
end
|
||||
|
||||
def test_pretty_state
|
||||
state = PRETTY_STATE_PROTOTYPE.dup
|
||||
assert_equal({
|
||||
:allow_nan => false,
|
||||
:array_nl => "\n",
|
||||
:ascii_only => false,
|
||||
:buffer_initial_length => 1024,
|
||||
:quirks_mode => false,
|
||||
:depth => 0,
|
||||
:indent => " ",
|
||||
:max_nesting => 100,
|
||||
:object_nl => "\n",
|
||||
:space => " ",
|
||||
:space_before => "",
|
||||
}.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
|
||||
end
|
||||
|
||||
def test_safe_state
|
||||
state = SAFE_STATE_PROTOTYPE.dup
|
||||
assert_equal({
|
||||
:allow_nan => false,
|
||||
:array_nl => "",
|
||||
:ascii_only => false,
|
||||
:buffer_initial_length => 1024,
|
||||
:quirks_mode => false,
|
||||
:depth => 0,
|
||||
:indent => "",
|
||||
:max_nesting => 100,
|
||||
:object_nl => "",
|
||||
:space => "",
|
||||
:space_before => "",
|
||||
}.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
|
||||
end
|
||||
|
||||
def test_fast_state
|
||||
state = FAST_STATE_PROTOTYPE.dup
|
||||
assert_equal({
|
||||
:allow_nan => false,
|
||||
:array_nl => "",
|
||||
:ascii_only => false,
|
||||
:buffer_initial_length => 1024,
|
||||
:quirks_mode => false,
|
||||
:depth => 0,
|
||||
:indent => "",
|
||||
:max_nesting => 0,
|
||||
:object_nl => "",
|
||||
:space => "",
|
||||
:space_before => "",
|
||||
}.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
|
||||
end
|
||||
|
||||
def test_allow_nan
|
||||
assert_raises(GeneratorError) { generate([JSON::NaN]) }
|
||||
assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { fast_generate([JSON::NaN]) }
|
||||
assert_raises(GeneratorError) { pretty_generate([JSON::NaN]) }
|
||||
assert_equal "[\n NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { generate([JSON::Infinity]) }
|
||||
assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { fast_generate([JSON::Infinity]) }
|
||||
assert_raises(GeneratorError) { pretty_generate([JSON::Infinity]) }
|
||||
assert_equal "[\n Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { generate([JSON::MinusInfinity]) }
|
||||
assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
|
||||
assert_raises(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
|
||||
assert_raises(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
|
||||
assert_equal "[\n -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
|
||||
end
|
||||
|
||||
def test_depth
|
||||
ary = []; ary << ary
|
||||
assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
|
||||
assert_raises(JSON::NestingError) { JSON.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) }
|
||||
assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
|
||||
s = JSON.state.new
|
||||
assert_equal 0, s.depth
|
||||
assert_raises(JSON::NestingError) { ary.to_json(s) }
|
||||
assert_equal 100, s.depth
|
||||
end
|
||||
|
||||
def test_buffer_initial_length
|
||||
s = JSON.state.new
|
||||
assert_equal 1024, s.buffer_initial_length
|
||||
s.buffer_initial_length = 0
|
||||
assert_equal 1024, s.buffer_initial_length
|
||||
s.buffer_initial_length = -1
|
||||
assert_equal 1024, s.buffer_initial_length
|
||||
s.buffer_initial_length = 128
|
||||
assert_equal 128, s.buffer_initial_length
|
||||
end
|
||||
|
||||
def test_gc
|
||||
if respond_to?(:assert_in_out_err)
|
||||
assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
|
||||
bignum_too_long_to_embed_as_string = 1234567890123456789012345
|
||||
expect = bignum_too_long_to_embed_as_string.to_s
|
||||
GC.stress = true
|
||||
|
||||
10.times do |i|
|
||||
tmp = bignum_too_long_to_embed_as_string.to_json
|
||||
raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end if GC.respond_to?(:stress=)
|
||||
|
||||
def test_configure_using_configure_and_merge
|
||||
numbered_state = {
|
||||
:indent => "1",
|
||||
:space => '2',
|
||||
:space_before => '3',
|
||||
:object_nl => '4',
|
||||
:array_nl => '5'
|
||||
}
|
||||
state1 = JSON.state.new
|
||||
state1.merge(numbered_state)
|
||||
assert_equal '1', state1.indent
|
||||
assert_equal '2', state1.space
|
||||
assert_equal '3', state1.space_before
|
||||
assert_equal '4', state1.object_nl
|
||||
assert_equal '5', state1.array_nl
|
||||
state2 = JSON.state.new
|
||||
state2.configure(numbered_state)
|
||||
assert_equal '1', state2.indent
|
||||
assert_equal '2', state2.space
|
||||
assert_equal '3', state2.space_before
|
||||
assert_equal '4', state2.object_nl
|
||||
assert_equal '5', state2.array_nl
|
||||
end
|
||||
|
||||
def test_configure_hash_conversion
|
||||
state = JSON.state.new
|
||||
state.configure(:indent => '1')
|
||||
assert_equal '1', state.indent
|
||||
state = JSON.state.new
|
||||
foo = 'foo'
|
||||
assert_raise(TypeError) do
|
||||
state.configure(foo)
|
||||
end
|
||||
def foo.to_h
|
||||
{ :indent => '2' }
|
||||
end
|
||||
state.configure(foo)
|
||||
assert_equal '2', state.indent
|
||||
end
|
||||
|
||||
if defined?(JSON::Ext::Generator)
|
||||
def test_broken_bignum # [ruby-core:38867]
|
||||
pid = fork do
|
||||
Bignum.class_eval do
|
||||
def to_s
|
||||
end
|
||||
end
|
||||
begin
|
||||
JSON::Ext::Generator::State.new.generate(1<<64)
|
||||
exit 1
|
||||
rescue TypeError
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
_, status = Process.waitpid2(pid)
|
||||
assert status.success?
|
||||
rescue NotImplementedError
|
||||
# forking to avoid modifying core class of a parent process and
|
||||
# introducing race conditions of tests are run in parallel
|
||||
end
|
||||
end
|
||||
|
||||
def test_hash_likeness_set_symbol
|
||||
state = JSON.state.new
|
||||
assert_equal nil, state[:foo]
|
||||
assert_equal nil.class, state[:foo].class
|
||||
assert_equal nil, state['foo']
|
||||
state[:foo] = :bar
|
||||
assert_equal :bar, state[:foo]
|
||||
assert_equal :bar, state['foo']
|
||||
state_hash = state.to_hash
|
||||
assert_kind_of Hash, state_hash
|
||||
assert_equal :bar, state_hash[:foo]
|
||||
end
|
||||
|
||||
def test_hash_likeness_set_string
|
||||
state = JSON.state.new
|
||||
assert_equal nil, state[:foo]
|
||||
assert_equal nil, state['foo']
|
||||
state['foo'] = :bar
|
||||
assert_equal :bar, state[:foo]
|
||||
assert_equal :bar, state['foo']
|
||||
state_hash = state.to_hash
|
||||
assert_kind_of Hash, state_hash
|
||||
assert_equal :bar, state_hash[:foo]
|
||||
end
|
||||
|
||||
def test_json_generate
|
||||
assert_raise JSON::GeneratorError do
|
||||
assert_equal true, JSON.generate(["\xea"])
|
||||
end
|
||||
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 '[""]', JSON.generate([s.new])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env ruby
|
||||
# encoding: utf-8
|
||||
|
||||
require 'test/unit'
|
||||
require File.join(File.dirname(__FILE__), 'setup_variant')
|
||||
class TestJSONGenericObject < Test::Unit::TestCase
|
||||
include JSON
|
||||
|
||||
def setup
|
||||
@go = GenericObject[ :a => 1, :b => 2 ]
|
||||
end
|
||||
|
||||
def test_attributes
|
||||
assert_equal 1, @go.a
|
||||
assert_equal 1, @go[:a]
|
||||
assert_equal 2, @go.b
|
||||
assert_equal 2, @go[:b]
|
||||
assert_nil @go.c
|
||||
assert_nil @go[:c]
|
||||
end
|
||||
|
||||
def test_generate_json
|
||||
switch_json_creatable do
|
||||
assert_equal @go, JSON(JSON(@go), :create_additions => true)
|
||||
end
|
||||
end
|
||||
|
||||
def test_parse_json
|
||||
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 1, l.a
|
||||
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)
|
||||
assert_equal 2, l.a.b
|
||||
end
|
||||
end
|
||||
|
||||
def test_from_hash
|
||||
result = GenericObject.from_hash(
|
||||
:foo => { :bar => { :baz => true }, :quux => [ { :foobar => true } ] })
|
||||
assert_kind_of GenericObject, result.foo
|
||||
assert_kind_of GenericObject, result.foo.bar
|
||||
assert_equal true, result.foo.bar.baz
|
||||
assert_kind_of GenericObject, result.foo.quux.first
|
||||
assert_equal true, result.foo.quux.first.foobar
|
||||
assert_equal true, GenericObject.from_hash(true)
|
||||
end
|
||||
|
||||
def test_json_generic_object_load
|
||||
empty = JSON::GenericObject.load(nil)
|
||||
assert_kind_of JSON::GenericObject, empty
|
||||
simple_json = '{"json_class":"JSON::GenericObject","hello":"world"}'
|
||||
simple = JSON::GenericObject.load(simple_json)
|
||||
assert_kind_of JSON::GenericObject, simple
|
||||
assert_equal "world", simple.hello
|
||||
converting = JSON::GenericObject.load('{ "hello": "world" }')
|
||||
assert_kind_of JSON::GenericObject, converting
|
||||
assert_equal "world", converting.hello
|
||||
|
||||
json = JSON::GenericObject.dump(JSON::GenericObject[:hello => 'world'])
|
||||
assert_equal JSON(json), JSON('{"json_class":"JSON::GenericObject","hello":"world"}')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def switch_json_creatable
|
||||
JSON::GenericObject.json_creatable = true
|
||||
yield
|
||||
ensure
|
||||
JSON::GenericObject.json_creatable = false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
#!/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
|
|
@ -0,0 +1,72 @@
|
|||
#!/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
|
|
@ -225,4 +225,5 @@ class TestRDocGeneratorDarkfish < RDoc::TestCase
|
|||
"#{filename} is not hard-linked"
|
||||
end
|
||||
|
||||
end if defined?(JSON)
|
||||
end
|
||||
|
||||
|
|
|
@ -320,4 +320,5 @@ class TestRDocGeneratorJsonIndex < RDoc::TestCase
|
|||
assert_equal 'cd', @g.search_string('C d')
|
||||
end
|
||||
|
||||
end if defined?(JSON)
|
||||
end
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class TestRDocRubygemsHook < Gem::TestCase
|
|||
@hook.document 'darkfish', options, @a.doc_dir('rdoc')
|
||||
|
||||
assert @hook.rdoc_installed?
|
||||
end if defined?(JSON)
|
||||
end
|
||||
|
||||
def test_generate
|
||||
FileUtils.mkdir_p @a.doc_dir
|
||||
|
@ -105,7 +105,7 @@ class TestRDocRubygemsHook < Gem::TestCase
|
|||
assert_equal %w[README lib], rdoc.options.files.sort
|
||||
|
||||
assert_equal 'MyTitle', rdoc.store.main
|
||||
end if defined?(JSON)
|
||||
end
|
||||
|
||||
def test_generate_configuration_rdoc_array
|
||||
Gem.configuration[:rdoc] = %w[-A]
|
||||
|
@ -248,3 +248,4 @@ class TestRDocRubygemsHook < Gem::TestCase
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -531,4 +531,5 @@ class TestRDocServlet < RDoc::TestCase
|
|||
store.save
|
||||
end
|
||||
|
||||
end if defined?(JSON)
|
||||
end
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ class TestExtLibs < Test::Unit::TestCase
|
|||
check_existence "io/console"
|
||||
check_existence "io/nonblock"
|
||||
check_existence "io/wait"
|
||||
check_existence "json"
|
||||
check_existence "mathn/complex"
|
||||
check_existence "mathn/rational"
|
||||
check_existence "nkf"
|
||||
|
|
|
@ -379,7 +379,7 @@ ERROR: Possible alternatives: non_existent_with_hint
|
|||
wait_for_child_process_to_exit
|
||||
|
||||
assert_path_exists File.join(a2.doc_dir, 'ri')
|
||||
assert_path_exists File.join(a2.doc_dir, 'rdoc') if defined?(JSON)
|
||||
assert_path_exists File.join(a2.doc_dir, 'rdoc')
|
||||
end
|
||||
|
||||
def test_execute_saves_build_args
|
||||
|
|
|
@ -265,7 +265,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
|
|||
a2 = @specs['a-2']
|
||||
|
||||
assert_path_exists File.join(a2.doc_dir, 'rdoc')
|
||||
end if defined?(JSON)
|
||||
end
|
||||
|
||||
def test_execute_named
|
||||
spec_fetcher do |fetcher|
|
||||
|
@ -540,3 +540,4 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче