javabean_xml 0.1.0
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.
- data/lib/javabean_xml.rb +129 -0
- data/test/test_javabean_xml.rb +294 -0
- metadata +114 -0
data/lib/javabean_xml.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "builder"
|
3
|
+
|
4
|
+
# TODO : document
|
5
|
+
|
6
|
+
class JavabeanXml
|
7
|
+
|
8
|
+
def self.default_transformations
|
9
|
+
Hash.new { |hash, key|
|
10
|
+
lambda { |value, properties|
|
11
|
+
{
|
12
|
+
:class => key,
|
13
|
+
:value => value,
|
14
|
+
:properties => properties
|
15
|
+
}.delete_if { |k,v| v.nil? || v.respond_to?(:empty?) && v.empty? }
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_xml xml, type_transformations = {}
|
21
|
+
nodes = Nokogiri::XML(xml).xpath("/java/object")
|
22
|
+
# clean out all empty text-nodes to simplify parsing
|
23
|
+
nodes.xpath("//text()").each do |text_node|
|
24
|
+
if text_node.content.strip.empty?
|
25
|
+
text_node.remove
|
26
|
+
end
|
27
|
+
end
|
28
|
+
JavabeanXml.new(default_transformations.merge(type_transformations)).parse(nodes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.to_xml object, type_transformations = {}
|
32
|
+
xml = Builder::XmlMarkup.new #:indent => 2
|
33
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
34
|
+
xml.java :version => "1.6.0_26", :class => "java.beans.XMLDecoder" do
|
35
|
+
JavabeanXml.new(type_transformations).to_xml(object, xml)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize type_transformations
|
40
|
+
@type_transformations = type_transformations
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse node
|
44
|
+
if node.is_a? Nokogiri::XML::NodeSet
|
45
|
+
raise "structure error" if node.length != 1
|
46
|
+
node = node.first
|
47
|
+
end
|
48
|
+
case node.name
|
49
|
+
when "object"
|
50
|
+
parse_object node
|
51
|
+
else
|
52
|
+
parse_value node
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_xml object, xml
|
57
|
+
if object.is_a? Hash
|
58
|
+
case object[:class]
|
59
|
+
when Symbol
|
60
|
+
to_value_xml object, xml
|
61
|
+
else
|
62
|
+
to_object_xml object, xml
|
63
|
+
end
|
64
|
+
else
|
65
|
+
# puts "#{object.inspect} (#{object.class})"
|
66
|
+
# puts " -- #{@type_transformations.keys.inspect}"
|
67
|
+
transformation = @type_transformations[object.class]
|
68
|
+
raise "No transformation registered for #{object.class}" unless transformation
|
69
|
+
to_xml transformation[object], xml
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def parse_object obj_node
|
75
|
+
klass = obj_node["class"]
|
76
|
+
value = []
|
77
|
+
properties = {}
|
78
|
+
obj_node.children.each do |prop|
|
79
|
+
case prop.name
|
80
|
+
when "void"
|
81
|
+
properties[prop[:property].to_sym] = parse(prop.children)
|
82
|
+
when "string", "int", "boolean", "long"
|
83
|
+
value << parse_value(prop)
|
84
|
+
else
|
85
|
+
raise "Unsupported method: #{prop.inspect}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
value = value.first if value.length == 1
|
89
|
+
@type_transformations[klass][value, properties]
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_value val_node
|
93
|
+
case val_node.name
|
94
|
+
when "int", "long"
|
95
|
+
value = val_node.text.to_i
|
96
|
+
when "boolean"
|
97
|
+
value = val_node.text.strip == "true"
|
98
|
+
else
|
99
|
+
value = val_node.text
|
100
|
+
end
|
101
|
+
klass = val_node.name.to_sym
|
102
|
+
@type_transformations[klass][value, nil]
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_value_xml object, xml
|
106
|
+
xml.tag! object[:class] do
|
107
|
+
xml.text! object[:value].to_s
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_object_xml object, xml
|
112
|
+
xml.object :class => object[:class] do
|
113
|
+
if value = object[:value]
|
114
|
+
value = [value] unless value.is_a? Array
|
115
|
+
value.each do |v|
|
116
|
+
to_xml v, xml
|
117
|
+
end
|
118
|
+
end
|
119
|
+
if object[:properties]
|
120
|
+
object[:properties].each_pair do |p, val|
|
121
|
+
xml.void :property => p do
|
122
|
+
to_xml val, xml
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,294 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "test/unit"
|
3
|
+
require "equivalent-xml"
|
4
|
+
require "javabean_xml.rb"
|
5
|
+
class JavabeanXmlTest < Test::Unit::TestCase
|
6
|
+
@@test_xml_1 = <<-XML_END.split(/\n/).map{|l|l.gsub(/^( ){3}/, "")}.join("\n")
|
7
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
8
|
+
<java version="1.6.0_26" class="java.beans.XMLDecoder">
|
9
|
+
<object class="java.SomeClass">
|
10
|
+
<void property="myString">
|
11
|
+
<string>string1</string>
|
12
|
+
</void>
|
13
|
+
<void property="myObject">
|
14
|
+
<object class="java.NestedObject">
|
15
|
+
<string>Initialization string</string>
|
16
|
+
</object>
|
17
|
+
</void>
|
18
|
+
<void property="myDate">
|
19
|
+
<object class="java.util.Date">
|
20
|
+
<long>1314102227000</long>
|
21
|
+
</object>
|
22
|
+
</void>
|
23
|
+
</object>
|
24
|
+
</java>
|
25
|
+
XML_END
|
26
|
+
@@test_object_1_a = {
|
27
|
+
:class => "java.SomeClass",
|
28
|
+
:properties => {
|
29
|
+
:myString => {
|
30
|
+
:class => :string,
|
31
|
+
:value => "string1"
|
32
|
+
},
|
33
|
+
:myObject => {
|
34
|
+
:class => "java.NestedObject",
|
35
|
+
:value => {
|
36
|
+
:class => :string,
|
37
|
+
:value => "Initialization string"
|
38
|
+
}
|
39
|
+
},
|
40
|
+
:myDate => {
|
41
|
+
:class => "java.util.Date",
|
42
|
+
:value => {
|
43
|
+
:class => :long,
|
44
|
+
:value => 1314102227000
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
@@test_object_1_b = {
|
50
|
+
:class => "java.SomeClass",
|
51
|
+
:properties => {
|
52
|
+
:myString => {
|
53
|
+
:class => :string,
|
54
|
+
:value => "string1"
|
55
|
+
},
|
56
|
+
:myObject => {
|
57
|
+
:class => "java.NestedObject",
|
58
|
+
:value => {
|
59
|
+
:class => :string,
|
60
|
+
:value => "Initialization string"
|
61
|
+
}
|
62
|
+
},
|
63
|
+
:myDate => Time.at(1314102227)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
@@test_xml_2 = <<-XML_END.split(/\n/).map{|l|l.gsub(/^( ){3}/, "")}.join("\n")
|
68
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
69
|
+
<java version="1.6.0_26" class="java.beans.XMLDecoder">
|
70
|
+
<object class="java.SomeClass">
|
71
|
+
<void property="myString">
|
72
|
+
<string>string1</string>
|
73
|
+
</void>
|
74
|
+
<void property="myRectangle">
|
75
|
+
<object class="java.awt.Rectangle">
|
76
|
+
<int>0</int>
|
77
|
+
<int>2</int>
|
78
|
+
<int>200</int>
|
79
|
+
<int>300</int>
|
80
|
+
</object>
|
81
|
+
</void>
|
82
|
+
<void property="myDate">
|
83
|
+
<object class="java.util.Date">
|
84
|
+
<long>1314102227000</long>
|
85
|
+
</object>
|
86
|
+
</void>
|
87
|
+
</object>
|
88
|
+
</java>
|
89
|
+
XML_END
|
90
|
+
|
91
|
+
@@test_object_2_a = {
|
92
|
+
:class => "java.SomeClass",
|
93
|
+
:properties => {
|
94
|
+
:myString => {
|
95
|
+
:class => :string,
|
96
|
+
:value => "string1"
|
97
|
+
},
|
98
|
+
:myRectangle => {
|
99
|
+
:class => "java.awt.Rectangle",
|
100
|
+
:value => [
|
101
|
+
{
|
102
|
+
:class => :int,
|
103
|
+
:value => 0
|
104
|
+
},
|
105
|
+
{
|
106
|
+
:class => :int,
|
107
|
+
:value => 2
|
108
|
+
},
|
109
|
+
{
|
110
|
+
:class => :int,
|
111
|
+
:value => 200
|
112
|
+
},
|
113
|
+
{
|
114
|
+
:class => :int,
|
115
|
+
:value => 300
|
116
|
+
},
|
117
|
+
]
|
118
|
+
},
|
119
|
+
:myDate => {
|
120
|
+
:class => "java.util.Date",
|
121
|
+
:value => {
|
122
|
+
:class => :long,
|
123
|
+
:value => 1314102227000
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
Rect = Struct.new :values
|
130
|
+
MyClass = Struct.new :myString, :myRectangle, :myDate
|
131
|
+
|
132
|
+
@@test_object_2_b = \
|
133
|
+
MyClass.new(
|
134
|
+
"string1",
|
135
|
+
Rect.new([0,2,200,300]),
|
136
|
+
Time.at(1314102227)
|
137
|
+
)
|
138
|
+
|
139
|
+
def test_deserialization_simple
|
140
|
+
assert_equal(
|
141
|
+
@@test_object_1_a,
|
142
|
+
JavabeanXml.from_xml(@@test_xml_1),
|
143
|
+
"Should parse simple object"
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def test_deserialization_multiple_initialization_values
|
149
|
+
assert_equal(
|
150
|
+
@@test_object_2_a,
|
151
|
+
JavabeanXml.from_xml(@@test_xml_2),
|
152
|
+
"Should handle several initialization parameters"
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_deserialization_custom_object_types_simple
|
157
|
+
assert_equal(
|
158
|
+
@@test_object_1_b,
|
159
|
+
JavabeanXml.from_xml(
|
160
|
+
@@test_xml_1,
|
161
|
+
:long => lambda { |value, properties|
|
162
|
+
value.to_i
|
163
|
+
},
|
164
|
+
"java.util.Date" => lambda { |value, properties|
|
165
|
+
Time.at(value/1000)
|
166
|
+
}
|
167
|
+
),
|
168
|
+
"Should parse object and transform them"
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_deserialization_custom_object_types_complex
|
173
|
+
assert_equal(
|
174
|
+
@@test_object_2_b,
|
175
|
+
JavabeanXml.from_xml(
|
176
|
+
@@test_xml_2,
|
177
|
+
:long => lambda { |value, properties|
|
178
|
+
value.to_i
|
179
|
+
},
|
180
|
+
"java.util.Date" => lambda { |value, properties|
|
181
|
+
Time.at(value/1000)
|
182
|
+
},
|
183
|
+
:int => lambda { |value, properties|
|
184
|
+
value.to_i
|
185
|
+
},
|
186
|
+
:string => lambda { |value, properties|
|
187
|
+
value
|
188
|
+
},
|
189
|
+
"java.awt.Rectangle" => lambda { |value, properties|
|
190
|
+
Rect.new value
|
191
|
+
},
|
192
|
+
"java.SomeClass" => lambda { |value, properties|
|
193
|
+
c = MyClass.new
|
194
|
+
properties.each_pair do |k, v|
|
195
|
+
c.send :"#{k}=", v
|
196
|
+
end
|
197
|
+
c
|
198
|
+
}
|
199
|
+
),
|
200
|
+
"Should parse object and transform them"
|
201
|
+
)
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_serialization_simple
|
205
|
+
assert(
|
206
|
+
EquivalentXml.equivalent?(
|
207
|
+
@@test_xml_1,
|
208
|
+
JavabeanXml.to_xml(@@test_object_1_a),
|
209
|
+
:element_order => (RUBY_VERSION =~ /^1.9/),
|
210
|
+
:normalize_whitespace => true
|
211
|
+
),
|
212
|
+
"Should serialize simple object"
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_serialization_simple_with_transformation
|
217
|
+
result = JavabeanXml.to_xml(
|
218
|
+
@@test_object_1_b,
|
219
|
+
Time => lambda { |value|
|
220
|
+
{
|
221
|
+
:class => "java.util.Date",
|
222
|
+
:value => {
|
223
|
+
:class => :long,
|
224
|
+
:value => value.to_i * 1000
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
)
|
229
|
+
unless EquivalentXml.equivalent?(
|
230
|
+
@@test_xml_1,
|
231
|
+
result,
|
232
|
+
:element_order => (RUBY_VERSION =~ /^1.9/),
|
233
|
+
:normalize_whitespace => true
|
234
|
+
)
|
235
|
+
then
|
236
|
+
puts "Expected:"
|
237
|
+
puts @@test_xml_2
|
238
|
+
puts
|
239
|
+
puts "Was:"
|
240
|
+
puts result
|
241
|
+
fail "Should serialize object and transform types"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_serialization_complex_with_transformation
|
246
|
+
result = JavabeanXml.to_xml(
|
247
|
+
@@test_object_2_b,
|
248
|
+
Rect => lambda { |value|
|
249
|
+
{
|
250
|
+
:class => "java.awt.Rectangle",
|
251
|
+
:value => value.values.map {|v|
|
252
|
+
{
|
253
|
+
:class => :int,
|
254
|
+
:value => v
|
255
|
+
}
|
256
|
+
}
|
257
|
+
}
|
258
|
+
},
|
259
|
+
String => lambda { |value|
|
260
|
+
{ :class => :string, :value => value }
|
261
|
+
},
|
262
|
+
Time => lambda { |value|
|
263
|
+
{
|
264
|
+
:class => "java.util.Date",
|
265
|
+
:value => {
|
266
|
+
:class => :long,
|
267
|
+
:value => value.to_i * 1000
|
268
|
+
}
|
269
|
+
}
|
270
|
+
},
|
271
|
+
MyClass => lambda { |value|
|
272
|
+
{
|
273
|
+
:class => "java.SomeClass",
|
274
|
+
:properties => value.each_pair.inject({}){|h, (k, v)| h[k] = v; h}
|
275
|
+
}
|
276
|
+
}
|
277
|
+
)
|
278
|
+
unless EquivalentXml.equivalent?(
|
279
|
+
@@test_xml_2,
|
280
|
+
result,
|
281
|
+
:element_order => (RUBY_VERSION =~ /^1.9/),
|
282
|
+
:normalize_whitespace => true
|
283
|
+
)
|
284
|
+
then
|
285
|
+
puts "Expected:"
|
286
|
+
puts @@test_xml_2
|
287
|
+
puts
|
288
|
+
puts "Was:"
|
289
|
+
puts result
|
290
|
+
fail "Should serialize object and transform types"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: javabean_xml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Einar Magn\xC3\xBAs Boson"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-12-22 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: nokogiri
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 5
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 5
|
33
|
+
version: "1.5"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: builder
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 7
|
45
|
+
segments:
|
46
|
+
- 3
|
47
|
+
- 0
|
48
|
+
version: "3.0"
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: equivalent-xml
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 15
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
- 2
|
63
|
+
version: "0.2"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
description: Small library for encoding and decoding xml the way java.beans.{Encoder,Decoder} does it
|
67
|
+
email: einar.boson@gmail.com
|
68
|
+
executables: []
|
69
|
+
|
70
|
+
extensions: []
|
71
|
+
|
72
|
+
extra_rdoc_files: []
|
73
|
+
|
74
|
+
files:
|
75
|
+
- lib/javabean_xml.rb
|
76
|
+
- test/test_javabean_xml.rb
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/einarmagnus/javabean_xml
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 57
|
92
|
+
segments:
|
93
|
+
- 1
|
94
|
+
- 8
|
95
|
+
- 7
|
96
|
+
version: 1.8.7
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.6.2
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Small library for interacting with java.beans.{Encoder,Decoder}
|
113
|
+
test_files:
|
114
|
+
- test/test_javabean_xml.rb
|