jrjackson 0.3.9-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.__jrubyrc +433 -0
  3. data/.gitignore +10 -0
  4. data/.mvn/extensions.xml +8 -0
  5. data/Gemfile +10 -0
  6. data/Mavenfile +32 -0
  7. data/README.md +150 -0
  8. data/Rakefile +10 -0
  9. data/alt_bench.rb +46 -0
  10. data/changelog.md +131 -0
  11. data/jrjackson.gemspec +32 -0
  12. data/lib/com/fasterxml/jackson/core/jackson-annotations/2.7.1/jackson-annotations-2.7.1.jar +0 -0
  13. data/lib/com/fasterxml/jackson/core/jackson-core/2.7.1/jackson-core-2.7.1.jar +0 -0
  14. data/lib/com/fasterxml/jackson/core/jackson-databind/2.7.1/jackson-databind-2.7.1.jar +0 -0
  15. data/lib/com/fasterxml/jackson/module/jackson-module-afterburner/2.6.3/jackson-module-afterburner-2.6.3.jar +0 -0
  16. data/lib/com/fasterxml/jackson/module/jackson-module-afterburner/2.7.1/jackson-module-afterburner-2.7.1.jar +0 -0
  17. data/lib/jrjackson.rb +2 -0
  18. data/lib/jrjackson/build_info.rb +15 -0
  19. data/lib/jrjackson/jars/jrjackson-1.2.18.jar +0 -0
  20. data/lib/jrjackson/jars/jrjackson-1.2.7.jar +0 -0
  21. data/lib/jrjackson/jrjackson.rb +94 -0
  22. data/lib/jrjackson_jars.rb +7 -0
  23. data/lib/require_relative_patch.rb +6 -0
  24. data/pom.xml +193 -0
  25. data/profiling/profiled.rb +15 -0
  26. data/run_all_individual_bench.sh +25 -0
  27. data/run_jruby_individual_bench.sh +20 -0
  28. data/run_mri_individual_bench.sh +7 -0
  29. data/src/main/java/com/jrjackson/IParseHandler.java +53 -0
  30. data/src/main/java/com/jrjackson/JavaBigDecimalValueConverter.java +17 -0
  31. data/src/main/java/com/jrjackson/JavaBigIntValueConverter.java +17 -0
  32. data/src/main/java/com/jrjackson/JavaConverter.java +10 -0
  33. data/src/main/java/com/jrjackson/JavaFloatValueConverter.java +16 -0
  34. data/src/main/java/com/jrjackson/JavaHandler.java +118 -0
  35. data/src/main/java/com/jrjackson/JavaLongValueConverter.java +16 -0
  36. data/src/main/java/com/jrjackson/JjParse.java +147 -0
  37. data/src/main/java/com/jrjackson/JrJacksonBase.java +152 -0
  38. data/src/main/java/com/jrjackson/JrJacksonJava.java +81 -0
  39. data/src/main/java/com/jrjackson/JrJacksonRaw.java +108 -0
  40. data/src/main/java/com/jrjackson/JrJacksonRuby.java +89 -0
  41. data/src/main/java/com/jrjackson/JrJacksonSaj.java +26 -0
  42. data/src/main/java/com/jrjackson/JrJacksonSch.java +25 -0
  43. data/src/main/java/com/jrjackson/JrJacksonService.java +38 -0
  44. data/src/main/java/com/jrjackson/JrParse.java +149 -0
  45. data/src/main/java/com/jrjackson/ParseError.java +16 -0
  46. data/src/main/java/com/jrjackson/RubyAnySerializer.java +254 -0
  47. data/src/main/java/com/jrjackson/RubyBigDecimalValueConverter.java +18 -0
  48. data/src/main/java/com/jrjackson/RubyBigIntValueConverter.java +21 -0
  49. data/src/main/java/com/jrjackson/RubyConverter.java +12 -0
  50. data/src/main/java/com/jrjackson/RubyDateFormat.java +34 -0
  51. data/src/main/java/com/jrjackson/RubyFloatValueConverter.java +18 -0
  52. data/src/main/java/com/jrjackson/RubyHandler.java +119 -0
  53. data/src/main/java/com/jrjackson/RubyIntValueConverter.java +18 -0
  54. data/src/main/java/com/jrjackson/RubyJacksonModule.java +72 -0
  55. data/src/main/java/com/jrjackson/RubyKeyConverter.java +12 -0
  56. data/src/main/java/com/jrjackson/RubyNameConverter.java +9 -0
  57. data/src/main/java/com/jrjackson/RubyObjectDeserializer.java +182 -0
  58. data/src/main/java/com/jrjackson/RubyStringConverter.java +18 -0
  59. data/src/main/java/com/jrjackson/RubyStringKeyConverter.java +15 -0
  60. data/src/main/java/com/jrjackson/RubyStringNameConverter.java +12 -0
  61. data/src/main/java/com/jrjackson/RubySymbolKeyConverter.java +15 -0
  62. data/src/main/java/com/jrjackson/RubySymbolNameConverter.java +12 -0
  63. data/src/main/java/com/jrjackson/RubyUtils.java +150 -0
  64. data/src/main/java/com/jrjackson/SajParse.java +169 -0
  65. data/src/main/java/com/jrjackson/SchParse.java +209 -0
  66. data/src/main/java/com/jrjackson/StreamParse.java +66 -0
  67. data/test/jrjackson_test.rb +533 -0
  68. metadata +162 -0
@@ -0,0 +1,169 @@
1
+ package com.jrjackson;
2
+
3
+ import com.fasterxml.jackson.core.JsonParser;
4
+ import com.fasterxml.jackson.core.JsonProcessingException;
5
+ import com.fasterxml.jackson.core.JsonStreamContext;
6
+ import com.fasterxml.jackson.core.JsonLocation;
7
+
8
+ import java.io.IOException;
9
+
10
+ import org.jruby.internal.runtime.methods.DynamicMethod;
11
+ import org.jruby.runtime.ThreadContext;
12
+ import org.jruby.runtime.builtin.IRubyObject;
13
+ import org.jruby.exceptions.RaiseException;
14
+
15
+ /**
16
+ *
17
+ * @author Guy Boertje
18
+ */
19
+ public class SajParse extends StreamParse {
20
+
21
+ protected DynamicMethod _error;
22
+ protected boolean _no_error;
23
+
24
+ public SajParse(ThreadContext ctx, IRubyObject handler)
25
+ throws RaiseException {
26
+ super(ctx, handler);
27
+
28
+ if (_no_add_value) {
29
+ throw ParseError.newParseError(_ruby, "Handler does not implement public API");
30
+ }
31
+ _error = _meta.searchMethod("error");
32
+ _no_error = _error.isUndefined();
33
+
34
+ }
35
+
36
+ @Override
37
+ public IRubyObject deserialize(JsonParser jp) {
38
+
39
+ try {
40
+ while(jp.nextValue() != null) {
41
+ handleCurrentToken(jp);
42
+ }
43
+ }
44
+ catch (JsonProcessingException e) {
45
+ if(!_no_error) {
46
+ JsonLocation location = e.getLocation();
47
+ IRubyObject message = RubyUtils.rubyString(_ruby, e.getMessage());
48
+ IRubyObject line = RubyUtils.rubyFixnum(_ruby, location.getLineNr());
49
+ IRubyObject column = RubyUtils.rubyFixnum(_ruby, location.getColumnNr());
50
+
51
+ _error.call(_ctx, _handler, _meta, "error", message, line, column);
52
+ }
53
+ }
54
+ catch (IOException e) {
55
+ if(!_no_error) {
56
+ IRubyObject message = RubyUtils.rubyString(_ruby, e.getMessage());
57
+ IRubyObject line = RubyUtils.rubyFixnum(_ruby, 1);
58
+ IRubyObject column = RubyUtils.rubyFixnum(_ruby, 1);
59
+
60
+ _error.call(_ctx, _handler, _meta, "error", message, line, column);
61
+ }
62
+ }
63
+
64
+ return _ctx.nil;
65
+ }
66
+
67
+ private void callAddValue(IRubyObject val, IRubyObject key) {
68
+ _add_value.call(_ctx, _handler, _meta, "add_value", val, key);
69
+ }
70
+
71
+ private IRubyObject getParentName(JsonStreamContext x) {
72
+ String parentName = x.getCurrentName();
73
+ IRubyObject parent = _ctx.nil;
74
+
75
+ if (!x.inRoot()) {
76
+ parentName = x.getParent().getCurrentName();
77
+ }
78
+ if (parentName != null) {
79
+ parent = RubyUtils.rubyString(_ruby, parentName);
80
+ }
81
+ return parent;
82
+ }
83
+
84
+ private IRubyObject getFieldName(JsonStreamContext x) {
85
+ String currentName = x.getCurrentName();
86
+ IRubyObject name = _ctx.nil;
87
+
88
+ if (currentName != null) {
89
+ name = RubyUtils.rubyString(_ruby, currentName);
90
+ }
91
+ return name;
92
+ }
93
+
94
+ private void handleCurrentToken(JsonParser jp)
95
+ throws IOException, JsonProcessingException {
96
+
97
+ JsonStreamContext cx = jp.getParsingContext();
98
+
99
+ IRubyObject value;
100
+
101
+ switch (jp.getCurrentToken()) {
102
+ case START_OBJECT:
103
+ if (!_no_hash_start) {
104
+ _hash_start.call(_ctx, _handler, _meta, "hash_start", getParentName(cx));
105
+ }
106
+ break;
107
+
108
+ case START_ARRAY:
109
+ if (!_no_array_start) {
110
+ _array_start.call(_ctx, _handler, _meta, "array_start", getParentName(cx));
111
+ }
112
+ break;
113
+
114
+ case FIELD_NAME:
115
+ break;
116
+
117
+ case VALUE_EMBEDDED_OBJECT:
118
+ value = RubyUtils.rubyObject(_ruby, jp.getEmbeddedObject());
119
+ callAddValue(value, getFieldName(cx));
120
+ break;
121
+
122
+ case VALUE_STRING:
123
+ value = keyConverter.convert(_ruby, jp);
124
+ callAddValue(value, getFieldName(cx));
125
+ break;
126
+
127
+ case VALUE_NUMBER_INT:
128
+ /* [JACKSON-100]: caller may want to get all integral values
129
+ * returned as BigInteger, for consistency
130
+ */
131
+ JsonParser.NumberType numberType = jp.getNumberType();
132
+ value = RubyUtils.rubyBignum(_ruby, jp.getBigIntegerValue());
133
+ callAddValue(value, getFieldName(cx));
134
+ break;
135
+
136
+ case VALUE_NUMBER_FLOAT:
137
+ value = RubyUtils.rubyBigDecimal(_ruby, jp.getDecimalValue());
138
+ callAddValue(value, getFieldName(cx));
139
+ break;
140
+
141
+ case VALUE_TRUE:
142
+ value = _ruby.newBoolean(Boolean.TRUE);
143
+ callAddValue(value, getFieldName(cx));
144
+ break;
145
+
146
+ case VALUE_FALSE:
147
+ value = _ruby.newBoolean(Boolean.FALSE);
148
+ callAddValue(value, getFieldName(cx));
149
+ break;
150
+
151
+ case VALUE_NULL: // should not get this but...
152
+ value = _ctx.nil;
153
+ callAddValue(value, getFieldName(cx));
154
+ break;
155
+
156
+ case END_ARRAY:
157
+ if (!_no_array_end) {
158
+ _array_end.call(_ctx, _handler, _meta, "array_end", getFieldName(cx));
159
+ }
160
+ break;
161
+
162
+ case END_OBJECT:
163
+ if (!_no_hash_end) {
164
+ _hash_end.call(_ctx, _handler, _meta, "hash_end", getFieldName(cx));
165
+ }
166
+ break;
167
+ }
168
+ }
169
+ }
@@ -0,0 +1,209 @@
1
+ package com.jrjackson;
2
+
3
+ import com.fasterxml.jackson.core.JsonParser;
4
+ import com.fasterxml.jackson.core.JsonProcessingException;
5
+ import com.fasterxml.jackson.core.JsonStreamContext;
6
+ import java.io.IOException;
7
+ import java.util.HashMap;
8
+
9
+ import org.jruby.internal.runtime.methods.DynamicMethod;
10
+ import org.jruby.runtime.ThreadContext;
11
+ import org.jruby.runtime.builtin.IRubyObject;
12
+ import org.jruby.RubyString;
13
+ import org.jruby.exceptions.RaiseException;
14
+
15
+ /**
16
+ *
17
+ * @author Guy Boertje
18
+ */
19
+ public class SchParse extends StreamParse {
20
+
21
+ protected final DynamicMethod _hash_key;
22
+ protected final boolean _no_hash_key;
23
+
24
+ protected final DynamicMethod _hash_set;
25
+ protected final boolean _no_hash_set;
26
+
27
+ protected final DynamicMethod _array_append;
28
+ protected final boolean _no_array_append;
29
+
30
+ protected final HashMap<JsonStreamContext, IRubyObject> _objectMap = new HashMap<JsonStreamContext, IRubyObject>();
31
+ protected JsonStreamContext _deepestContext;
32
+
33
+ public SchParse(ThreadContext ctx, IRubyObject handler)
34
+ throws RaiseException {
35
+ super(ctx, handler);
36
+
37
+ _hash_key = _meta.searchMethod("hash_key");
38
+ _no_hash_key = _hash_key.isUndefined();
39
+
40
+ _hash_set = _meta.searchMethod("hash_set");
41
+ _no_hash_set = _hash_set.isUndefined();
42
+
43
+ _array_append = _meta.searchMethod("array_append");
44
+ _no_array_append = _array_append.isUndefined();
45
+
46
+ //_hash_key is optional
47
+ if (
48
+ _no_hash_start || _no_hash_end ||
49
+ _no_array_start || _no_array_end ||
50
+ _no_add_value || _no_hash_set || _no_array_append
51
+ ) {
52
+ throw ParseError.newParseError(_ruby, "Handler does not implement public API");
53
+ }
54
+ }
55
+
56
+ @Override
57
+ public IRubyObject deserialize(JsonParser jp) throws RaiseException {
58
+ try {
59
+
60
+ while (jp.nextValue() != null) {
61
+ handleCurrentToken(jp);
62
+ }
63
+ return _ctx.nil;
64
+
65
+ } catch (JsonProcessingException e) {
66
+ throw ParseError.newParseError(_ruby, e.getLocalizedMessage());
67
+ } catch (IOException e) {
68
+ throw _ruby.newIOError(e.getLocalizedMessage());
69
+ }
70
+ }
71
+
72
+ private void callAddValue(JsonStreamContext x) {
73
+ JsonStreamContext px = x.getParent();
74
+ IRubyObject target = _objectMap.get(x);
75
+ IRubyObject dtarget = _objectMap.get(_deepestContext);
76
+
77
+ if (px == null) {
78
+ _add_value.call(_ctx, _handler, _meta, "add_value", dtarget);
79
+ return;
80
+ }
81
+
82
+ if (x.inArray()) {
83
+ _array_append.call(_ctx, _handler, _meta, "array_append", target, dtarget);
84
+ } else if (x.inObject()) {
85
+ IRubyObject treatedKey = callHashKey(x);
86
+ _hash_set.call(_ctx, _handler, _meta, "hash_set", target, treatedKey, dtarget);
87
+ } else {
88
+ _add_value.call(_ctx, _handler, _meta, "add_value", target);
89
+ }
90
+ }
91
+
92
+ private void callAddValue(JsonStreamContext x, IRubyObject val) {
93
+ IRubyObject target = _objectMap.get(x);
94
+
95
+ if (x.inArray()) {
96
+ _array_append.call(_ctx, _handler, _meta, "array_append", target, val);
97
+ } else if (x.inObject()) {
98
+ IRubyObject treatedKey = callHashKey(x);
99
+ _hash_set.call(_ctx, _handler, _meta, "hash_set", target, treatedKey, val);
100
+ } else {
101
+ _add_value.call(_ctx, _handler, _meta, "add_value", val);
102
+ }
103
+ }
104
+
105
+ private IRubyObject callHashKey(JsonStreamContext x) {
106
+ String k = x.getCurrentName();
107
+ if (k == null) {
108
+ return _ctx.nil;
109
+ }
110
+ RubyString key = RubyUtils.rubyString(_ruby, k);
111
+ if (_no_hash_key) {
112
+ return key;
113
+ }
114
+ return _hash_key.call(_ctx, _handler, _meta, "hash_key", key);
115
+ }
116
+
117
+ private void handleCurrentToken(JsonParser jp)
118
+ throws IOException, JsonProcessingException {
119
+
120
+ JsonStreamContext cx = jp.getParsingContext();
121
+ IRubyObject value;
122
+ IRubyObject rubyObject;
123
+
124
+ switch (jp.getCurrentToken()) {
125
+ case START_OBJECT:
126
+ _deepestContext = cx;
127
+ rubyObject = _hash_start.call(_ctx, _handler, _meta, "hash_start");
128
+ _objectMap.put(cx, rubyObject);
129
+ break;
130
+
131
+ case START_ARRAY:
132
+ _deepestContext = cx;
133
+ rubyObject = _array_start.call(_ctx, _handler, _meta, "array_start");
134
+ _objectMap.put(cx, rubyObject);
135
+ break;
136
+
137
+ case FIELD_NAME:
138
+ break;
139
+
140
+ case VALUE_EMBEDDED_OBJECT:
141
+ value = RubyUtils.rubyObject(_ruby, jp.getEmbeddedObject());
142
+ callAddValue(cx, value);
143
+ break;
144
+
145
+ case VALUE_STRING:
146
+ value = keyConverter.convert(_ruby, jp);
147
+ callAddValue(cx, value);
148
+ break;
149
+
150
+ case VALUE_NUMBER_INT:
151
+ /* [JACKSON-100]: caller may want to get all integral values
152
+ * returned as BigInteger, for consistency
153
+ */
154
+ JsonParser.NumberType numberType = jp.getNumberType();
155
+ value = RubyUtils.rubyBignum(_ruby, jp.getBigIntegerValue());
156
+ callAddValue(cx, value);
157
+ break;
158
+
159
+ case VALUE_NUMBER_FLOAT:
160
+ value = RubyUtils.rubyBigDecimal(_ruby, jp.getDecimalValue());
161
+ callAddValue(cx, value);
162
+ break;
163
+
164
+ case VALUE_TRUE:
165
+ value = _ruby.newBoolean(Boolean.TRUE);
166
+ callAddValue(cx, value);
167
+ break;
168
+
169
+ case VALUE_FALSE:
170
+ value = _ruby.newBoolean(Boolean.FALSE);
171
+ callAddValue(cx, value);
172
+ break;
173
+
174
+ case VALUE_NULL: // should not get this but...
175
+ value = _ctx.nil;
176
+ callAddValue(cx, value);
177
+ break;
178
+
179
+ case END_ARRAY:
180
+ _array_end.call(_ctx, _handler, _meta, "array_end");
181
+ callAddValue(cx);
182
+ _deepestContext = cx;
183
+ break;
184
+
185
+ case END_OBJECT:
186
+ _hash_end.call(_ctx, _handler, _meta, "hash_end");
187
+ callAddValue(cx);
188
+ _deepestContext = cx;
189
+ break;
190
+ }
191
+
192
+ }
193
+
194
+ }
195
+ // System.out.println("--- callAddValue final ---");
196
+ // if (px != null) {
197
+ // System.out.println("-------- parent --------");
198
+ // System.out.println(px.getTypeDesc());
199
+ // System.out.println(px.getCurrentName());
200
+ // }
201
+ // System.out.println("-------- current --------");
202
+ // System.out.println(x.getTypeDesc());
203
+ // System.out.println(x.getCurrentName());
204
+ // System.out.println(target);
205
+ //
206
+ // System.out.println("-------- deepest --------");
207
+ // System.out.println(_deepestContext.getTypeDesc());
208
+ // System.out.println(_deepestContext.getCurrentName());
209
+ // System.out.println(dtarget);
@@ -0,0 +1,66 @@
1
+ package com.jrjackson;
2
+
3
+ import com.fasterxml.jackson.core.JsonParser;
4
+
5
+ import org.jruby.Ruby;
6
+ import org.jruby.RubyClass;
7
+ import org.jruby.internal.runtime.methods.DynamicMethod;
8
+ import org.jruby.runtime.ThreadContext;
9
+ import org.jruby.runtime.builtin.IRubyObject;
10
+ import org.jruby.exceptions.RaiseException;
11
+
12
+ /**
13
+ *
14
+ * @author Guy Boertje
15
+ */
16
+ public abstract class StreamParse {
17
+
18
+ protected final ThreadContext _ctx;
19
+ protected final Ruby _ruby;
20
+ protected final IRubyObject _handler;
21
+ protected final RubyClass _meta;
22
+ protected final RubyStringConverter keyConverter = new RubyStringConverter();
23
+
24
+ protected final DynamicMethod _hash_start;
25
+ protected final boolean _no_hash_start;
26
+
27
+ protected final DynamicMethod _hash_end;
28
+ protected final boolean _no_hash_end;
29
+
30
+ protected final DynamicMethod _array_start;
31
+ protected final boolean _no_array_start;
32
+
33
+ protected final DynamicMethod _array_end;
34
+ protected final boolean _no_array_end;
35
+
36
+ protected final DynamicMethod _add_value;
37
+ protected final boolean _no_add_value;
38
+
39
+ public StreamParse(ThreadContext ctx, IRubyObject handler)
40
+ throws RaiseException {
41
+
42
+ _ctx = ctx;
43
+ _ruby = ctx.runtime;
44
+ _handler = handler;
45
+ _meta = _handler.getMetaClass();
46
+
47
+ _add_value = _meta.searchMethod("add_value");
48
+ _no_add_value = _add_value.isUndefined();
49
+
50
+ _hash_start = _meta.searchMethod("hash_start");
51
+ _no_hash_start = _hash_start.isUndefined();
52
+
53
+ _hash_end = _meta.searchMethod("hash_end");
54
+ _no_hash_end = _hash_end.isUndefined();
55
+
56
+ _array_start = _meta.searchMethod("array_start");
57
+ _no_array_start = _array_start.isUndefined();
58
+
59
+ _array_end = _meta.searchMethod("array_end");
60
+ _no_array_end = _array_end.isUndefined();
61
+
62
+ }
63
+
64
+ public abstract IRubyObject deserialize(JsonParser jp)
65
+ throws RaiseException;
66
+ }
@@ -0,0 +1,533 @@
1
+ # encoding: UTF-8
2
+
3
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
4
+
5
+ require "java"
6
+
7
+ require 'securerandom'
8
+
9
+ require 'test/unit'
10
+ require 'thread'
11
+ require 'bigdecimal'
12
+ require 'jrjackson'
13
+ require 'stringio'
14
+ require 'time'
15
+ require 'date'
16
+
17
+ class JrJacksonTest < Test::Unit::TestCase
18
+ # def test_serialize_date
19
+ # # default date format
20
+ # time_string = "2014-12-18 18:18:18 +0000"
21
+ # source_time = Time.parse(time_string)
22
+ # # source_time = Date.today
23
+ # serialized_output = JrJackson::Json.dump({current_time: source_time})
24
+ # assert_equal %Q{{"current_time":"#{time_string}"}}, serialized_output
25
+ # end
26
+ # end
27
+ class Test::Unit::CustomObj
28
+ end
29
+
30
+ class ToJsonData
31
+ attr_reader :one, :two, :six
32
+ def initialize(a,b,c)
33
+ @one, @two, @six = a,b,c
34
+ end
35
+ def to_h
36
+ {'one' => one, 'two' => two, 'six' => six}
37
+ end
38
+ def to_json_data
39
+ [one, two, six]
40
+ end
41
+ end
42
+
43
+ class CustomToH
44
+ attr_accessor :one, :two, :six
45
+ def initialize(a,b,c)
46
+ @one, @two, @six = a,b,c
47
+ end
48
+ def to_h
49
+ {'one' => one, 'two' => two, 'six' => six}
50
+ end
51
+ end
52
+
53
+ class CustomToHash
54
+ attr_accessor :one, :two, :six
55
+ def initialize(a,b,c)
56
+ @one, @two, @six = a,b,c
57
+ end
58
+ def to_hash
59
+ {'one' => one, 'two' => two, 'six' => six}
60
+ end
61
+ end
62
+
63
+ class CustomToJson
64
+ attr_accessor :one, :two, :six
65
+ def initialize(a,b,c)
66
+ @one, @two, @six = a,b,c
67
+ end
68
+ def to_json
69
+ %Q|{"one":#{one},"two":#{two},"six":#{six}}|
70
+ end
71
+ end
72
+
73
+ class CustomToTime
74
+ def initialize(tm = Time.now)
75
+ @now = tm
76
+ end
77
+ def to_time
78
+ @now.to_time
79
+ end
80
+ end
81
+
82
+ CustomStruct = Struct.new(:one, :two, :six)
83
+
84
+
85
+ class ScHandler
86
+ attr_accessor :calls
87
+
88
+ def initialize(arr = [])
89
+ @calls = arr
90
+ end
91
+
92
+ def hash_start()
93
+ @calls << [:hash_start]
94
+ {}
95
+ end
96
+
97
+ def hash_end()
98
+ @calls << [:hash_end]
99
+ end
100
+
101
+ def hash_key(key)
102
+ @calls << [:hash_key, key]
103
+ return 'too' if 'two' == key
104
+ return :symbol if 'symbol' == key
105
+ key
106
+ end
107
+
108
+ def array_start()
109
+ @calls << [:array_start]
110
+ []
111
+ end
112
+
113
+ def array_end()
114
+ @calls << [:array_end]
115
+ end
116
+
117
+ def add_value(value)
118
+ @calls << [:add_value, value]
119
+ end
120
+
121
+ def hash_set(h, key, value)
122
+ # h[key] = value
123
+ @calls << [:hash_set, key, value]
124
+ end
125
+
126
+ def array_append(a, value)
127
+ # a.push(value)
128
+ @calls << [:array_append, value]
129
+ end
130
+ end
131
+
132
+ JsonString = %Q|{
133
+ "array": [
134
+ {
135
+ "num" : 3,
136
+ "string": "message",
137
+ "hash" : {
138
+ "h2" : {
139
+ "a" : [ 1, 2, 3 ]
140
+ }
141
+ }
142
+ }
143
+ ],
144
+ "boolean" : true
145
+ }|
146
+
147
+ def test_sc_parse
148
+ array = []
149
+ handler = ScHandler.new(array)
150
+ JrJackson::Json.sc_load(handler, JsonString)
151
+ assert_equal(
152
+ [
153
+ [:hash_start],
154
+ [:array_start],
155
+ [:hash_start],
156
+ [:hash_key, 'num'],
157
+ [:hash_set, "num", 3],
158
+ [:hash_key, 'string'],
159
+ [:hash_set, "string", "message"],
160
+ [:hash_start],
161
+ [:hash_start],
162
+ [:array_start],
163
+ [:array_append, 1],
164
+ [:array_append, 2],
165
+ [:array_append, 3],
166
+ [:array_end],
167
+ [:hash_key, "a"],
168
+ [:hash_set, "a", []],
169
+ [:hash_end],
170
+ [:hash_key, "h2"],
171
+ [:hash_set, "h2", {}],
172
+ [:hash_end],
173
+ [:hash_key, "hash"],
174
+ [:hash_set, "hash", {}],
175
+ [:hash_end],
176
+ [:array_append, {}],
177
+ [:array_end],
178
+ [:hash_key, "array"],
179
+ [:hash_set, "array", []],
180
+ [:hash_key, 'boolean'],
181
+ [:hash_set, "boolean", true],
182
+ [:hash_end],
183
+ [:add_value, {}]
184
+ ],
185
+ handler.calls
186
+ )
187
+ end
188
+
189
+ class SjHandler
190
+ attr_reader :calls
191
+ def initialize(arr = []) @calls = arr; end
192
+ def hash_start(key) @calls << [:hash_start, key]; end
193
+ def hash_end(key) @calls << [:hash_end, key]; end
194
+ def array_start(key) @calls << [:array_start, key]; end
195
+ def array_end(key) @calls << [:array_end, key]; end
196
+ def add_value(value, key) @calls << [:add_value, value, key]; end
197
+ def error(message, line, column) @calls << [:error, message, line, column]; end
198
+ end
199
+
200
+ def test_sj_parse
201
+ handler = SjHandler.new()
202
+ JrJackson::Json.sj_load(handler, JsonString)
203
+ assert_equal(
204
+ [
205
+ [:hash_start, nil],
206
+ [:array_start, 'array'],
207
+ [:hash_start, nil],
208
+ [:add_value, 3, 'num'],
209
+ [:add_value, 'message', 'string'],
210
+ [:hash_start, 'hash'],
211
+ [:hash_start, 'h2'],
212
+ [:array_start, 'a'],
213
+ [:add_value, 1, nil],
214
+ [:add_value, 2, nil],
215
+ [:add_value, 3, nil],
216
+ [:array_end, 'a'],
217
+ [:hash_end, 'h2'],
218
+ [:hash_end, 'hash'],
219
+ [:hash_end, nil],
220
+ [:array_end, 'array'],
221
+ [:add_value, true, 'boolean'],
222
+ [:hash_end, nil]
223
+ ],
224
+ handler.calls
225
+ )
226
+ end
227
+
228
+ def test_to_json_data
229
+ object = ToJsonData.new("uno", :two, 6.0)
230
+ expected = "[1,[\"uno\",\"two\",6.0]]"
231
+ actual = JrJackson::Json.dump([1, object])
232
+ assert_equal expected, actual
233
+ end
234
+
235
+ def test_datetime
236
+ h = {datetime: DateTime.parse("2014-01-27T18:24:46+01:00")}
237
+ expected = '{"datetime":"2014-01-27 17:24:46 +0000"}'
238
+ actual = JrJackson::Json.dump(h)
239
+ assert_equal expected, actual
240
+ end
241
+
242
+ def test_dump_date_in_array
243
+ expected = "[\"2016-04-10\"]"
244
+ td = Date.new(2016, 4, 10)
245
+ actual = JrJackson::Json.generate([td])
246
+ assert_equal(actual, expected)
247
+ end
248
+
249
+ def test_threading
250
+ q1, q2, q3 = Queue.new, Queue.new, Queue.new
251
+
252
+ s1 = %Q|{"a":2.5, "b":0.214, "c":3.4567, "d":-3.4567}|
253
+ th1 = Thread.new(s1) do |string|
254
+ q1 << JrJackson::Json.load(string, {use_bigdecimal: true, raw: true})
255
+ end
256
+ th2 = Thread.new(s1) do |string|
257
+ q2 << JrJackson::Json.load(string, {use_bigdecimal: true, symbolize_keys: true})
258
+ end
259
+ th3 = Thread.new(s1) do |string|
260
+ q3 << JrJackson::Json.load(string, {use_bigdecimal: false, symbolize_keys: true})
261
+ end
262
+ a1, a2, a3 = q1.pop, q2.pop, q3.pop
263
+ assert_equal ["a", "b", "c", "d"], a1.keys
264
+ assert a1.values.all? {|v| v.is_a?(Java::JavaMath::BigDecimal)}, "Expected all values to be Java::JavaMath::BigDecimal"
265
+ assert_equal [:a, :b, :c, :d], a2.keys
266
+ assert a2.values.all? {|v| v.is_a?(BigDecimal)}, "Expected all values to be BigDecimal"
267
+ assert a3.values.all? {|v| v.is_a?(Float)}, "Expected all values to be Float"
268
+ end
269
+
270
+ def test_deserialize_JSON_with_UTF8_characters
271
+ json_string = JrJackson::Json.dump({"utf8" => "żółć"})
272
+ expected = {utf8: "żółć"}
273
+ actual = JrJackson::Json.load(json_string, :symbolize_keys => true)
274
+
275
+ assert_equal expected, actual
276
+ end
277
+
278
+ def test_deserialize_JSON_with_two_entries
279
+ json_string = JrJackson::Json.dump({'foo' => 'foo_value', 'bar' => 'bar_value'})
280
+ expected = {foo: 'foo_value', bar: 'bar_value'}
281
+ actual = JrJackson::Json.load(json_string, :symbolize_keys => true)
282
+ assert_equal expected, actual
283
+ end
284
+
285
+ def test_serialize_non_json_datatypes_as_values
286
+ dt = Time.now
287
+ today = Date.today
288
+ co1 = CustomToH.new("uno", :two, 6.0)
289
+ co2 = CustomToHash.new("uno", :two, 6.0)
290
+ co3 = CustomToJson.new(1.0, 2, 6.0)
291
+ co4 = CustomStruct.new(1, 2, 6)
292
+ co5 = CustomToTime.new(today)
293
+ source = {'sym' => :a_symbol, 'dt' => dt, 'co1' => co1, 'co2' => co2, 'co3' => co3, 'co4' => co4, 'co5' => co5}
294
+ json_string = JrJackson::Json.dump(source)
295
+ expected = {
296
+ :sym => "a_symbol",
297
+ :dt => dt.to_s,
298
+ :co1 => {:one => "uno", :two => "two", :six => 6.0 },
299
+ :co2 => {:one => "uno", :two => "two", :six => 6.0 },
300
+ :co3 => {:one => 1.0, :two => 2.0, :six => 6.0 },
301
+ :co4 => [1, 2, 6],
302
+ :co5 => today.to_time.to_s
303
+ }
304
+ actual = JrJackson::Json.load(json_string, :symbolize_keys => true)
305
+ assert_equal expected, actual
306
+ end
307
+
308
+ def test_raw_serialize_base_classes
309
+ # String
310
+ assert_equal JrJackson::Json.dump("foo"), "\"foo\""
311
+
312
+ # Hash and implementations of the Java Hash interface
313
+ assert_equal JrJackson::Json.dump({"foo" => 1}), "{\"foo\":1}"
314
+ assert_equal JrJackson::Json.dump(Java::JavaUtil::HashMap.new({"foo" => 1})), "{\"foo\":1}"
315
+ assert_equal JrJackson::Json.dump(Java::JavaUtil::LinkedHashMap.new({"foo" => 1})), "{\"foo\":1}"
316
+
317
+ # Array and implementations of the Java List interface
318
+ assert_equal JrJackson::Json.dump(["foo", 1]), "[\"foo\",1]"
319
+ assert_equal JrJackson::Json.dump(Java::JavaUtil::ArrayList.new(["foo", 1])), "[\"foo\",1]"
320
+ assert_equal JrJackson::Json.dump(Java::JavaUtil::LinkedList.new(["foo", 1])), "[\"foo\",1]"
321
+ assert_equal JrJackson::Json.dump(Java::JavaUtil::Vector.new(["foo", 1])), "[\"foo\",1]"
322
+
323
+ # true/false
324
+ assert_equal JrJackson::Json.dump(true), "true"
325
+ assert_equal JrJackson::Json.dump(false), "false"
326
+
327
+ # nil
328
+ assert_equal JrJackson::Json.dump(nil), "null"
329
+ end
330
+
331
+ def test_serialize_date
332
+ # default date format
333
+ time_string = "2014-06-10 18:18:40 EDT"
334
+ source_time = Time.parse(time_string)
335
+ serialized_output = JrJackson::Json.dump({"time" => source_time})
336
+ other_time = Time.parse(serialized_output.split('"')[-2])
337
+ assert_equal other_time.to_f, source_time.to_f
338
+ end
339
+
340
+ def test_serialize_date_date_format
341
+
342
+ time = Time.new(2014,6,10,18,18,40, "-04:00")
343
+ # using date_format option
344
+ assert_equal "{\"time\":\"2014-06-10\"}", JrJackson::Json.dump({"time" => time}, :date_format => "yyyy-MM-dd")
345
+ assert_match /\{"time"\:"\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}[+-]\d{4}"\}/, JrJackson::Json.dump({"time" => time}, :date_format => "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
346
+ end
347
+
348
+ def test_serialize_date_date_format_timezone
349
+
350
+ time = Time.new(2014,6,10,18,18,40, "-04:00")
351
+ # using date_format and timezone options
352
+ assert_equal "{\"time\":\"2014-06-10T22:18:40.000+0000\"}", JrJackson::Json.dump({"time" => time}, :date_format => "yyyy-MM-dd'T'HH:mm:ss.SSSZ", :timezone => "UTC")
353
+ # iso8601 date_format and timezone
354
+ assert_equal "{\"time\":\"2014-06-10T22:18:40.000Z\"}", JrJackson::Json.dump({"time" => time}, :date_format => "yyyy-MM-dd'T'HH:mm:ss.SSSX", :timezone => "UTC")
355
+ end
356
+
357
+ def test_can_parse_returning_java_objects
358
+ expected = {"arr"=>[2, 3, 4],
359
+ "flo"=>0.333E1,
360
+ "moo"=>"bar",
361
+ "utf8"=>"żółć",
362
+ "zzz"=>{"bar"=>-9}}
363
+ json = '{"utf8":"żółć", "moo": "bar", "zzz": {"bar":-9}, "arr": [2,3,4], "flo": 3.33}'
364
+
365
+ actual = JrJackson::Json.parse_java(json)
366
+
367
+ assert_equal Java::JavaUtil::HashMap, actual.class
368
+ assert_equal Java::JavaUtil::ArrayList, actual["arr"].class
369
+ assert_equal Java::JavaMath::BigDecimal, actual["flo"].class
370
+ assert_equal "3.33", actual["flo"].to_s
371
+ assert_equal "bar", actual["moo"]
372
+ assert_equal "żółć", actual["utf8"]
373
+ assert_equal Java::JavaUtil::HashMap, actual["zzz"].class
374
+ assert_equal Bignum, actual["zzz"]["bar"].class
375
+ assert_equal -9, actual["zzz"]["bar"]
376
+ end
377
+
378
+ def test_can_parse_returning_ruby_objects_string_keys
379
+ expected = {
380
+ "a"=>"żółć", # string
381
+ "b"=>true, # boolean
382
+ "c"=>12345, # number
383
+ "d"=>
384
+ [true,
385
+ [false,
386
+ [-123456789, nil],
387
+ 0.39676E1,
388
+ ["Something else.", false],
389
+ nil]], # mix it up array
390
+ "e"=>{"zero"=>nil, "one"=>1, "two"=>2, "three"=>[3], "four"=>[0, 1, 2, 3, 4]}, # hash
391
+ "żółć"=>nil,# nil
392
+ "h"=>{"a"=>{"b"=>{"c"=>{"d"=>{"e"=>{"f"=>{"g"=>nil}}}}}}},# deep hash, not that deep
393
+ "i"=>[[[[[[[nil]]]]]]] # deep array, again, not that deep
394
+ }
395
+ json = JrJackson::Json.dump(expected)
396
+ actual = JrJackson::Json.parse_ruby(json)
397
+ assert_equal expected, actual
398
+ actual = JrJackson::Ruby.parse(json, {})
399
+ assert_equal expected, actual
400
+ end
401
+
402
+ def test_can_parse_returning_ruby_objects_symbol_keys
403
+ expected = {:a=>"Alpha",
404
+ :b=>true,
405
+ :c=>12345,
406
+ :d=>
407
+ [true, [false, [-123456789, nil], 3.9676, ["Something else.", false], nil]],
408
+ :e=>{:zero=>nil, :one=>1, :two=>2, :three=>[3], :four=>[0, 1, 2, 3, 4]},
409
+ :f=>nil,
410
+ :h=>{:a=>{:b=>{:c=>{:d=>{:e=>{:f=>{:g=>nil}}}}}}},
411
+ :i=>[[[[[[[nil]]]]]]]
412
+ }
413
+ json = JrJackson::Json.dump(expected)
414
+ actual = JrJackson::Ruby.parse_sym(json, {})
415
+ assert_equal expected, actual
416
+ end
417
+
418
+ def test_can_parse_nulls
419
+ expected = {"foo" => nil}
420
+ json = '{"foo":null}'
421
+ actual = JrJackson::Json.parse(json)
422
+ assert_equal expected, actual
423
+ end
424
+
425
+ def test_stringio
426
+ expected = {"foo" => 5, "utf8" => "żółć"}
427
+ json = ::StringIO.new('{"foo":5, "utf8":"żółć"}')
428
+ actual = JrJackson::Json.load_ruby(json)
429
+ assert_equal expected, actual
430
+ end
431
+
432
+ def test_ruby_io
433
+ expected = {"foo" => 5, "bar" => 6, "utf8" => "żółć"}
434
+ json, w = IO.pipe
435
+ w.write('{"foo":5, "bar":6, "utf8":"żółć"}')
436
+ w.close
437
+ actual = JrJackson::Json.load(json)
438
+ assert_equal expected, actual
439
+ end
440
+
441
+ def test_bad_utf
442
+ assert_raise JrJackson::ParseError do
443
+ JrJackson::Json.load("\x82\xAC\xEF")
444
+ end
445
+ end
446
+
447
+ def test_can_parse_bignum
448
+ expected = 12345678901234567890123456789
449
+ json = '{"foo":12345678901234567890123456789}'
450
+
451
+ actual = JrJackson::Json.parse(json)['foo']
452
+ assert_equal expected, actual
453
+ end
454
+
455
+ def test_can_parse_big_decimals
456
+ expected = BigDecimal.new '0.12345678901234567890123456789'
457
+ json = '{"foo":0.12345678901234567890123456789}'
458
+
459
+ actual = JrJackson::Json.parse(json, :use_bigdecimal => true)['foo']
460
+ assert_bigdecimal_equal expected, actual
461
+
462
+ actual = JrJackson::Json.parse(json, :use_bigdecimal => true, :symbolize_keys => true)[:foo]
463
+ assert_bigdecimal_equal expected, actual
464
+
465
+ actual = JrJackson::Java.parse(json, {})['foo']
466
+ assert_bigdecimal_similar expected, actual
467
+
468
+ actual = JrJackson::Json.parse(json, :use_bigdecimal => true, :raw => true)['foo']
469
+ assert_bigdecimal_similar expected, actual
470
+
471
+ end
472
+
473
+ def test_cannot_serialize_object
474
+ err = assert_raises(JrJackson::ParseError) { JrJackson::Json.dump({"foo" => Object.new}) }
475
+ assert_match /Cannot serialize instance of: Object/, err.message
476
+ end
477
+
478
+ def test_cannot_serialize_basic_object
479
+ err = assert_raises(JrJackson::ParseError) { JrJackson::Json.dump({"foo" => BasicObject.new}) }
480
+ assert_match /Cannot serialize instance of: BasicObject/, err.message
481
+ end
482
+
483
+ def test_cannot_serialize_custom_object
484
+ err = assert_raises(JrJackson::ParseError) { JrJackson::Json.dump({"foo" => Test::Unit::CustomObj.new}) }
485
+ assert_match /Cannot serialize instance of: Test::Unit::CustomObj/, err.message
486
+ end
487
+
488
+ def test_supports_pretty_printing
489
+ object = {"foo" => 5, "utf8" => "żółć"}
490
+
491
+ actual = JrJackson::Json.dump(object, :pretty => true)
492
+ assert_equal "{\n \"foo\" : 5,\n \"utf8\" : \"żółć\"\n}", actual
493
+ end
494
+
495
+ def test_can_serialise_non_string_keys
496
+ object = {5 => "foo"}
497
+ actual = JrJackson::Json.dump(object)
498
+ assert_equal "{\"5\":\"foo\"}", actual
499
+ end
500
+
501
+ def test_can_serialise_regex_match_data
502
+ re = %r|(foo)(bar)(baz)|
503
+ m = re.match('foobarbaz')
504
+ object = {'matched' => m[2]}
505
+ actual = JrJackson::Json.dump(object)
506
+ assert_equal "{\"matched\":\"bar\"}", actual
507
+ end
508
+
509
+ def test_can_mix_java_and_ruby_objects
510
+ json = '{"utf8":"żółć", "moo": "bar", "arr": [2,3,4], "flo": 3.33}'
511
+ timeobj = Time.new(2015,11,11,11,11,11).utc
512
+ expected = '{"mixed":{"arr":[2,3,4],"utf8":"żółć","flo":3.33,"zzz":{"one":1.0,"two":2,"six":6.0},"moo":"bar"},"time":"2015-11-11 11:11:11 +0000"}'
513
+ object = JrJackson::Json.parse_java(json)
514
+ object["zzz"] = CustomToJson.new(1.0, 2, 6.0)
515
+ mixed = {"mixed" => object}
516
+ mixed['time'] = timeobj
517
+ actual = JrJackson::Json.dump(mixed)
518
+ assert_equal expected, actual
519
+ end
520
+
521
+ # -----------------------------
522
+
523
+ def assert_bigdecimal_equal(expected, actual)
524
+ assert_equal expected, actual
525
+ assert_equal expected.class, actual.class
526
+ assert_equal BigDecimal, actual.class
527
+ end
528
+
529
+ def assert_bigdecimal_similar(expected, actual)
530
+ assert_equal BigDecimal.new(expected.to_s).round(11), BigDecimal.new(actual.to_s).round(11)
531
+ assert_equal Java::JavaMath::BigDecimal, actual.class
532
+ end
533
+ end