gson-class-generator 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/gson-class-generator +75 -0
- data/lib/gson-class-generator.rb +426 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31edff5ae4bbb5516c78e6de582d195b766171b7
|
4
|
+
data.tar.gz: 9a52c4c0cecb7523f40cc91e4081a6708e5752e6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: efb5fbdcbd12fe87cd37cd0a26e51c6438375d03bed6e4bdeddd3376408a9ef109df6f496a516e5485bc91f78d7fdd47785c0027e74af492c16cb6cc1bb01c24
|
7
|
+
data.tar.gz: 41b5df16e9ba959c8b3c62978763ac4fd8105ab6a69a3b7400a39552af664fc8c6d251eaeda79d424c353a273430673e6dc23149353ff057b5b7274018f9ac7f
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/gson-class-generator.rb'
|
4
|
+
|
5
|
+
module GsonClassGeneratorExecutor
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def self.abort_with_help(message = nil)
|
10
|
+
puts "#{message}\n\n" if message
|
11
|
+
puts <<-HELP
|
12
|
+
Usage:
|
13
|
+
#{File.basename(__FILE__)} [-gfscb] <json_file> <class_name> <output_dir>
|
14
|
+
|
15
|
+
Flags:
|
16
|
+
g - Generate getters.
|
17
|
+
s - Generate setters.
|
18
|
+
f - All fields are final, this will also generate a public no arguement constructor for all classes and the 's' flag is ignored.
|
19
|
+
c - A public constructor is generated for all the classes with all the fields as arguments.
|
20
|
+
b - All primative types are boxed.
|
21
|
+
|
22
|
+
Arguments:
|
23
|
+
json_file - The file you want to generate the model class from.
|
24
|
+
class_name - The top level class name, and therefore the name of the output file.
|
25
|
+
output_directory - The location to save the generated model class. If this is not provided the current directory will be used.
|
26
|
+
HELP
|
27
|
+
abort
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.check_flags(args)
|
31
|
+
if (args.match(/^[gsfcb]+$/) === nil)
|
32
|
+
abort_with_help("Unknown argument: " + args.gsub(/[gsfcb]/, ""))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.get_opts(flags)
|
37
|
+
{
|
38
|
+
:getters => !(flags.match(/^-.*g.*/) === nil),
|
39
|
+
:setters => !(flags.match(/^-.*s.*/) === nil),
|
40
|
+
:final_fields => !(flags.match(/^-.*f.*/) === nil),
|
41
|
+
:field_constructor => !(flags.match(/^-.*c.*/) === nil),
|
42
|
+
:boxed_primatives => !(flags.match(/^-.*b.*/) === nil)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
public
|
47
|
+
|
48
|
+
def self.run(args)
|
49
|
+
|
50
|
+
if (args.length == 0)
|
51
|
+
abort_with_help("No arguments given")
|
52
|
+
end
|
53
|
+
|
54
|
+
args_offset = 0
|
55
|
+
flags = args[0]
|
56
|
+
if (flags.match(/^-.+$/))
|
57
|
+
check_flags(flags.gsub(/^-/, ""))
|
58
|
+
args_offset += 1
|
59
|
+
else
|
60
|
+
flags = ""
|
61
|
+
end
|
62
|
+
|
63
|
+
opts = self.get_opts(flags)
|
64
|
+
|
65
|
+
json_file = args[args_offset + 0]
|
66
|
+
class_name = args[args_offset + 1]
|
67
|
+
output_directory = args.fetch(args_offset + 2, "")
|
68
|
+
|
69
|
+
outputted_filename = GSONClassGenerator::generate(json_file, class_name, output_directory, opts)
|
70
|
+
|
71
|
+
puts "Generated '#{outputted_filename}'"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
GsonClassGeneratorExecutor::run(ARGV)
|
@@ -0,0 +1,426 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module GSONClassGenerator
|
4
|
+
|
5
|
+
public
|
6
|
+
|
7
|
+
def self.generate(json_filename, class_name, output_directory, opts = {})
|
8
|
+
|
9
|
+
begin
|
10
|
+
json = JSON.parse(File.read(File.expand_path(json_filename)))
|
11
|
+
rescue Exception => e
|
12
|
+
abort "Invalid JSON: #{e.message}"
|
13
|
+
end
|
14
|
+
|
15
|
+
java_class = parse(json_filename, class_name)
|
16
|
+
output_options = OutputOptions.new(opts)
|
17
|
+
|
18
|
+
output(java_class, output_directory, output_options)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
GSON_SERIALIZED_NAME_IMPORT = "import com.google.gson.annotations.SerializedName;\n"
|
24
|
+
|
25
|
+
def self.parse(json_filename, class_name)
|
26
|
+
json = JSON.parse(File.read(File.expand_path(json_filename)))
|
27
|
+
JavaClass.new(class_name, json)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.output(java_class, output_directory, opts)
|
31
|
+
|
32
|
+
filename = File.join(File.expand_path(output_directory), "#{java_class.class_name}.java")
|
33
|
+
|
34
|
+
File.open(filename, "w") do |file|
|
35
|
+
|
36
|
+
file << GSON_SERIALIZED_NAME_IMPORT
|
37
|
+
file << "\n"
|
38
|
+
file << "\n"
|
39
|
+
|
40
|
+
java_class.write(file, opts)
|
41
|
+
end
|
42
|
+
|
43
|
+
filename
|
44
|
+
end
|
45
|
+
|
46
|
+
class OutputOptions
|
47
|
+
|
48
|
+
def initialize(opts)
|
49
|
+
@getters = opts.fetch("getters".to_sym, false) === true
|
50
|
+
@setters = opts.fetch("setters".to_sym, false) === true
|
51
|
+
@final_fields = opts.fetch("final_fields".to_sym, false) === true
|
52
|
+
@field_constructor = opts.fetch("field_constructor".to_sym, false) === true
|
53
|
+
@boxed_primatives = opts.fetch("boxed_primatives".to_sym, false) === true
|
54
|
+
|
55
|
+
# Can't have setters for final fields
|
56
|
+
@setters = false if @final_fields
|
57
|
+
end
|
58
|
+
|
59
|
+
def getters?
|
60
|
+
@getters
|
61
|
+
end
|
62
|
+
|
63
|
+
def setters?
|
64
|
+
@setters
|
65
|
+
end
|
66
|
+
|
67
|
+
def final_fields?
|
68
|
+
@final_fields
|
69
|
+
end
|
70
|
+
|
71
|
+
def field_constructor?
|
72
|
+
@field_constructor
|
73
|
+
end
|
74
|
+
|
75
|
+
def boxed_primatives?
|
76
|
+
@boxed_primatives
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class JavaClass
|
81
|
+
|
82
|
+
public
|
83
|
+
|
84
|
+
attr_reader :class_name, :fields
|
85
|
+
|
86
|
+
def initialize(class_name, json, depth = 0)
|
87
|
+
@class_name = class_name
|
88
|
+
@nested_classes = Array.new
|
89
|
+
@fields = Array.new
|
90
|
+
@depth = depth
|
91
|
+
|
92
|
+
if (json.is_a?(Array))
|
93
|
+
|
94
|
+
# Pull out first object to examine what it looks like
|
95
|
+
first_json = json.first
|
96
|
+
fields = self.class.new(nil, first_json).fields
|
97
|
+
|
98
|
+
@fields += fields
|
99
|
+
|
100
|
+
else
|
101
|
+
|
102
|
+
json.each do |k, v|
|
103
|
+
field = JavaField.new(k, v)
|
104
|
+
|
105
|
+
if (field.custom_class?)
|
106
|
+
nested_name = self.class.get_nested_class_name(field)
|
107
|
+
nested_class = self.class.new(nested_name, v, depth + 1)
|
108
|
+
@nested_classes << nested_class
|
109
|
+
end
|
110
|
+
|
111
|
+
@fields << field
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def write(stream, opts)
|
118
|
+
tabs = self.class.indentation(@depth)
|
119
|
+
field_tabs = self.class.indentation(@depth + 1)
|
120
|
+
|
121
|
+
stream << "#{tabs}#{class_definition}\n"
|
122
|
+
stream << "#{tabs}{\n"
|
123
|
+
write_field_declarations(stream, field_tabs, opts)
|
124
|
+
write_constructor(stream, field_tabs, opts)
|
125
|
+
write_field_accessors(stream, field_tabs, opts)
|
126
|
+
write_nested_classes(stream, opts)
|
127
|
+
stream << "#{tabs}}\n"
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def self.get_nested_class_name(field)
|
133
|
+
class_name = field.java_class
|
134
|
+
if (field.is_array?)
|
135
|
+
# Remove 's' if array
|
136
|
+
class_name.chomp!("s")
|
137
|
+
end
|
138
|
+
class_name
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.indentation(depth)
|
142
|
+
Array.new(depth, "\t").join
|
143
|
+
end
|
144
|
+
|
145
|
+
def class_definition
|
146
|
+
prefix = "public"
|
147
|
+
if (@depth > 0)
|
148
|
+
prefix += " static"
|
149
|
+
end
|
150
|
+
"#{prefix} final class #{@class_name}"
|
151
|
+
end
|
152
|
+
|
153
|
+
def write_field_declarations(stream, tabs, opts)
|
154
|
+
@fields.each do |f|
|
155
|
+
stream << "\n"
|
156
|
+
f.write_declaration(stream, tabs, opts)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def write_constructor(stream, tabs, opts)
|
161
|
+
|
162
|
+
constructor_tabs = tabs + "\t"
|
163
|
+
|
164
|
+
if (opts.field_constructor?)
|
165
|
+
parameters = @fields.map { |f| "final #{f.type_definition(opts)} #{f.name}" }
|
166
|
+
stream << "\n"
|
167
|
+
stream << tabs
|
168
|
+
stream << "public #{class_name}(#{parameters.join(', ')})\n"
|
169
|
+
stream << tabs
|
170
|
+
stream << "{\n"
|
171
|
+
@fields.each do |f|
|
172
|
+
f.write_constructor_assignment(stream, constructor_tabs, opts)
|
173
|
+
end
|
174
|
+
stream << tabs
|
175
|
+
stream << "}\n"
|
176
|
+
end
|
177
|
+
if (opts.final_fields?)
|
178
|
+
|
179
|
+
stream << "\n"
|
180
|
+
stream << tabs
|
181
|
+
stream << "public #{class_name}()\n"
|
182
|
+
stream << tabs
|
183
|
+
stream << "{\n"
|
184
|
+
if (opts.field_constructor?)
|
185
|
+
arguments = @fields.map { |f| f.default_initalized_object(opts) }
|
186
|
+
# Call public constructor instead
|
187
|
+
stream << constructor_tabs
|
188
|
+
stream << "this(#{arguments.join(', ')});\n"
|
189
|
+
else
|
190
|
+
@fields.each do |f|
|
191
|
+
f.write_default_constructor_assignment(stream, constructor_tabs, opts)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
stream << tabs
|
195
|
+
stream << "}\n"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def write_field_accessors(stream, tabs, opts)
|
200
|
+
@fields.each do |f|
|
201
|
+
f.write_accessors(stream, tabs, opts)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def write_nested_classes(stream, opts)
|
206
|
+
@nested_classes.each do |nested_class|
|
207
|
+
stream << "\n"
|
208
|
+
nested_class.write(stream, opts)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class JavaField
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
JAVA_TYPES = {
|
218
|
+
FalseClass => "boolean",
|
219
|
+
TrueClass => "boolean",
|
220
|
+
Integer => "int",
|
221
|
+
Float => "float",
|
222
|
+
String => "String",
|
223
|
+
NilClass => "Object"
|
224
|
+
}
|
225
|
+
|
226
|
+
JAVA_TYPES_BOXED = {
|
227
|
+
"boolean" => "Boolean",
|
228
|
+
"int" => "Integer",
|
229
|
+
"float" => "Float"
|
230
|
+
}
|
231
|
+
|
232
|
+
JAVA_DEFAULT_INITIALIZED_PRIMATIVES = {
|
233
|
+
"boolean" => "false",
|
234
|
+
"int" => "0",
|
235
|
+
"float" => "0.0f",
|
236
|
+
}
|
237
|
+
|
238
|
+
JAVA_KEYWORDS = ["public", "protected", "private", "abstract", "final", "static", "strictfp", "transient", "volatile", "class", "enum", "interface", "extends", "implements", "void", "while", "do", "for", "if", "switch", "case", "default", "break", "continue", "return", "synchronized", "try", "catch", "throw", "throws", "finally", "super", "this", "new", "false", "true", "null", "instanceof", "package", "import", "assert", "boolean", "byte", "char", "short", "int", "long", "float", "double", "const", "goto"]
|
239
|
+
|
240
|
+
public
|
241
|
+
|
242
|
+
attr_reader :name, :java_class
|
243
|
+
|
244
|
+
def initialize(key, value)
|
245
|
+
@array_dimens = self.class.array_dimens(value)
|
246
|
+
@json_key = key
|
247
|
+
@name = self.class.javaify_key(key, self.is_array?)
|
248
|
+
@java_class = self.class.class_for_value(key, value)
|
249
|
+
@custom_class = !JAVA_TYPES.value?(@java_class)
|
250
|
+
end
|
251
|
+
|
252
|
+
def is_array?
|
253
|
+
@array_dimens > 0
|
254
|
+
end
|
255
|
+
|
256
|
+
def custom_class?
|
257
|
+
@custom_class
|
258
|
+
end
|
259
|
+
|
260
|
+
def write_declaration(stream, tabs, opts)
|
261
|
+
stream << tabs
|
262
|
+
stream << serialized_name_definition
|
263
|
+
stream << tabs
|
264
|
+
stream << field_definition(opts)
|
265
|
+
end
|
266
|
+
|
267
|
+
def write_default_constructor_assignment(stream, tabs, opts)
|
268
|
+
stream << tabs
|
269
|
+
stream << "#{@name} = #{default_initalized_object(opts)};\n"
|
270
|
+
end
|
271
|
+
|
272
|
+
def write_constructor_assignment(stream, tabs, opts)
|
273
|
+
stream << tabs
|
274
|
+
stream << "this.#{@name} = #{@name};\n"
|
275
|
+
end
|
276
|
+
|
277
|
+
def write_accessors(stream, tabs, opts)
|
278
|
+
if (opts.getters?)
|
279
|
+
write_getter_definition(stream, tabs)
|
280
|
+
end
|
281
|
+
if (opts.setters?)
|
282
|
+
write_setter_definition(stream, tabs)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def type_definition(opts)
|
287
|
+
class_name = @java_class
|
288
|
+
if (opts.boxed_primatives?)
|
289
|
+
class_name = JAVA_TYPES_BOXED.fetch(class_name, @java_class)
|
290
|
+
end
|
291
|
+
array_def = Array.new(@array_dimens, "[]").join
|
292
|
+
"#{class_name}#{array_def}"
|
293
|
+
end
|
294
|
+
|
295
|
+
def default_initalized_object(opts)
|
296
|
+
if (opts.boxed_primatives?)
|
297
|
+
"null"
|
298
|
+
else
|
299
|
+
JAVA_DEFAULT_INITIALIZED_PRIMATIVES.fetch(@java_class, "null")
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
private
|
304
|
+
|
305
|
+
def serialized_name_definition
|
306
|
+
"@SerializedName(\"#{@json_key}\")\n"
|
307
|
+
end
|
308
|
+
|
309
|
+
def field_definition(opts)
|
310
|
+
definition = "private"
|
311
|
+
if (opts.final_fields?)
|
312
|
+
definition += " final"
|
313
|
+
end
|
314
|
+
type_definition = type_definition(opts)
|
315
|
+
"#{definition} #{type_definition} #{@name};\n"
|
316
|
+
end
|
317
|
+
|
318
|
+
def accessor_name
|
319
|
+
@name.gsub(/^(.)/) { $1.upcase }
|
320
|
+
end
|
321
|
+
|
322
|
+
def write_getter_definition(stream, tabs)
|
323
|
+
stream << "\n"
|
324
|
+
stream << tabs
|
325
|
+
stream << "public final #{@type_definition} get#{accessor_name}()\n"
|
326
|
+
stream << tabs
|
327
|
+
stream << "{\n"
|
328
|
+
stream << tabs
|
329
|
+
stream << "\treturn #{@name};\n"
|
330
|
+
stream << tabs
|
331
|
+
stream << "}\n"
|
332
|
+
end
|
333
|
+
|
334
|
+
def write_setter_definition(stream, tabs)
|
335
|
+
stream << "\n"
|
336
|
+
stream << tabs
|
337
|
+
stream << "public final void set#{accessor_name}(final #{@type_definition} #{@name})\n"
|
338
|
+
stream << tabs
|
339
|
+
stream << "{\n"
|
340
|
+
stream << tabs
|
341
|
+
stream << "\tthis.#{@name} = #{@name};\n"
|
342
|
+
stream << tabs
|
343
|
+
stream << "}\n"
|
344
|
+
end
|
345
|
+
|
346
|
+
# STATIC METHODS
|
347
|
+
|
348
|
+
def self.array_dimens(value)
|
349
|
+
|
350
|
+
dimens = 0
|
351
|
+
|
352
|
+
array = value
|
353
|
+
while (array.is_a?(Array))
|
354
|
+
dimens += 1
|
355
|
+
array = array.first
|
356
|
+
end
|
357
|
+
|
358
|
+
dimens
|
359
|
+
end
|
360
|
+
|
361
|
+
def self.class_for_value(key, value)
|
362
|
+
|
363
|
+
java_class = nil
|
364
|
+
|
365
|
+
if (value.is_a?(Hash))
|
366
|
+
# Use key as class name
|
367
|
+
java_class = key
|
368
|
+
|
369
|
+
# Clean up first character
|
370
|
+
java_class = java_class.gsub(/^[^\p{Pc}\p{Alpha}\p{Sc}]/, "")
|
371
|
+
|
372
|
+
# Clean up allowed characters
|
373
|
+
java_class = java_class.gsub(/[^\p{Pc}\p{Alnum}\p{Sc}]/, "")
|
374
|
+
|
375
|
+
# Upper case it
|
376
|
+
java_class = java_class.gsub(/^([\p{Pc}\p{Sc}]*)(\p{Alpha})/) { "#{$1}#{$2.upcase}" }
|
377
|
+
|
378
|
+
elsif (value.is_a?(Array))
|
379
|
+
|
380
|
+
# First check that the array is a native one
|
381
|
+
first_object = value.first
|
382
|
+
|
383
|
+
java_class = self.class_for_value(key, first_object)
|
384
|
+
else
|
385
|
+
|
386
|
+
JAVA_TYPES.each do |ruby_cls, java_cls|
|
387
|
+
if (value.is_a?(ruby_cls))
|
388
|
+
java_class = java_cls
|
389
|
+
break
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
abort "No class is able to be determined for JSON key #{key} (ruby class: #{value.class})" if !java_class
|
395
|
+
|
396
|
+
java_class
|
397
|
+
end
|
398
|
+
|
399
|
+
def self.javaify_key(json_key, is_array)
|
400
|
+
field_name = json_key
|
401
|
+
|
402
|
+
if (is_array)
|
403
|
+
if (!field_name.end_with?("s"))
|
404
|
+
# Append 's' if it doesn't have it
|
405
|
+
field_name += "s"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Clean up first character
|
410
|
+
field_name = field_name.gsub(/^[^\p{Pc}\p{Alnum}\p{Sc}]+/, "")
|
411
|
+
|
412
|
+
# Clean up allowed characters
|
413
|
+
field_name = field_name.gsub(/[^\p{Pc}\p{Alnum}\p{Sc}]/, "_")
|
414
|
+
|
415
|
+
# Camel case underscores
|
416
|
+
field_name = field_name.gsub(/(?!^_)_+(.)/) { $1.upcase }
|
417
|
+
|
418
|
+
# Clean up field name in case its a Java keyword
|
419
|
+
if (!(JAVA_KEYWORDS.index(field_name) === nil))
|
420
|
+
field_name += "_"
|
421
|
+
end
|
422
|
+
|
423
|
+
field_name
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gson-class-generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rich Hodgkins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description:
|
28
|
+
email: github@rhodgkins.co.uk
|
29
|
+
executables:
|
30
|
+
- gson-class-generator
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/gson-class-generator.rb
|
35
|
+
- bin/gson-class-generator
|
36
|
+
homepage: https://github.com/rhodgkins/GSONClassGenerator
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.0.6
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Ruby script generating JSON model objects in Java when using GSON
|
60
|
+
test_files: []
|