jrjackson 0.3.9-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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