shale 0.4.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/LICENSE.txt +1 -1
- data/README.md +449 -40
- data/exe/shaleb +30 -6
- data/lib/shale/adapter/json.rb +3 -3
- data/lib/shale/adapter/nokogiri/document.rb +97 -0
- data/lib/shale/adapter/nokogiri/node.rb +100 -0
- data/lib/shale/adapter/nokogiri.rb +17 -156
- data/lib/shale/adapter/ox/document.rb +90 -0
- data/lib/shale/adapter/ox/node.rb +97 -0
- data/lib/shale/adapter/ox.rb +14 -138
- data/lib/shale/adapter/rexml/document.rb +98 -0
- data/lib/shale/adapter/rexml/node.rb +99 -0
- data/lib/shale/adapter/rexml.rb +14 -154
- data/lib/shale/adapter/toml_rb.rb +34 -0
- data/lib/shale/error.rb +57 -2
- data/lib/shale/mapper.rb +61 -9
- data/lib/shale/mapping/descriptor/dict.rb +12 -1
- data/lib/shale/mapping/descriptor/xml.rb +12 -2
- data/lib/shale/mapping/dict.rb +26 -2
- data/lib/shale/mapping/xml.rb +52 -6
- data/lib/shale/schema/{json_compiler → compiler}/boolean.rb +1 -1
- data/lib/shale/schema/{json_compiler/object.rb → compiler/complex.rb} +11 -8
- data/lib/shale/schema/{json_compiler → compiler}/date.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/float.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/integer.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/property.rb +6 -6
- data/lib/shale/schema/{json_compiler → compiler}/string.rb +1 -1
- data/lib/shale/schema/{json_compiler → compiler}/time.rb +1 -1
- data/lib/shale/schema/compiler/value.rb +21 -0
- data/lib/shale/schema/compiler/xml_complex.rb +50 -0
- data/lib/shale/schema/compiler/xml_property.rb +73 -0
- data/lib/shale/schema/json_compiler.rb +32 -34
- data/lib/shale/schema/json_generator.rb +4 -6
- data/lib/shale/schema/xml_compiler.rb +919 -0
- data/lib/shale/schema/xml_generator/import.rb +2 -2
- data/lib/shale/schema/xml_generator.rb +11 -13
- data/lib/shale/schema.rb +16 -0
- data/lib/shale/type/complex.rb +763 -0
- data/lib/shale/type/value.rb +38 -9
- data/lib/shale/utils.rb +42 -7
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +22 -19
- data/shale.gemspec +3 -3
- metadata +26 -17
- data/lib/shale/schema/json_compiler/utils.rb +0 -52
- data/lib/shale/schema/json_compiler/value.rb +0 -13
- data/lib/shale/type/composite.rb +0 -331
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f3c860cf358f78476f8f22a86006694c3d9fd6cb554c945d7364bbf8bb70815
|
4
|
+
data.tar.gz: 5eba158f49fdcfb3012f2e4dcf804fdc4c8fe1e0c37e8e3dbb37972aa469c8bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b002a6837137fa024788e50b2b408ebd7d80f1fd73c7bf1182a2d0b0ab784a07cbda08147af703c4ded76efcb086958049c3d82573e5f885d1b1745f9312eb0c
|
7
|
+
data.tar.gz: e66ba2657a540ad6e9cebd98cf4a61a9ae4b31c0fc6ba1e8085493cc727eda48e2cb87515d399a2524cac8bb4d92932f57dd548afa2ba1d8fb5b97bb5c8ef1a7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
## [0.7.0] - [2022-08-09]
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- `only: []` and `except: []` options that allow to controll what attributes are rendered/parsed
|
5
|
+
- `render_nil: true` option that allows to render nil values
|
6
|
+
- Allow to pass a context object to extractor/generator methods
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- Pass whole document to methods for JSON/YAML/TOML so its behavior is consistent with XML mapping
|
10
|
+
- Convert splat arguments to keyword arguments
|
11
|
+
- RSpec: enable random spec execution and warnings
|
12
|
+
|
13
|
+
## [0.6.0] - 2022-07-05
|
14
|
+
|
15
|
+
### Added
|
16
|
+
- Support for TOML
|
17
|
+
- Support for CDATA nodes in XML documents
|
18
|
+
- Support for using custom models
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
- Allow to map XML content using methods
|
22
|
+
- Prevent adding default mapping after mapping block was declared
|
23
|
+
|
24
|
+
## [0.5.0] - 2022-06-28
|
25
|
+
|
26
|
+
### Added
|
27
|
+
- Allow to generate Shale model from XML Schema
|
28
|
+
|
29
|
+
### Changed
|
30
|
+
- Shale doesn't defaults to REXML anymore - XML adapter needs to be set explicitly
|
31
|
+
- Rename "JSONSchemaError" to "SchemaError"
|
32
|
+
- Rename "Composite" type to "Complex"
|
33
|
+
- Drop support for Ruby 2.6
|
34
|
+
|
1
35
|
## [0.4.0] - 2022-05-30
|
2
36
|
|
3
37
|
### Added
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
# Shale
|
2
2
|
|
3
|
-
Shale is a Ruby object mapper and serializer for JSON, YAML and XML.
|
4
|
-
It allows you to parse JSON, YAML and XML data and convert it into Ruby data structures,
|
5
|
-
as well as serialize data structures into JSON, YAML or XML.
|
3
|
+
Shale is a Ruby object mapper and serializer for JSON, YAML, TOML and XML.
|
4
|
+
It allows you to parse JSON, YAML, TOML and XML data and convert it into Ruby data structures,
|
5
|
+
as well as serialize data structures into JSON, YAML, TOML or XML.
|
6
|
+
|
7
|
+
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
6
8
|
|
7
9
|
## Features
|
8
10
|
|
9
|
-
* Convert JSON, YAML and XML to Ruby data model
|
10
|
-
* Convert Ruby data model to JSON, YAML and XML
|
11
|
+
* Convert JSON, YAML, TOML and XML to Ruby data model
|
12
|
+
* Convert Ruby data model to JSON, YAML, TOML and XML
|
11
13
|
* Generate JSON and XML Schema from Ruby models
|
12
|
-
* Compile JSON Schema into Ruby models
|
13
|
-
* Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers
|
14
|
+
* Compile JSON and XML Schema into Ruby models
|
15
|
+
* Out of the box support for JSON, YAML, Tomlib, toml-rb, Nokogiri, REXML and Ox parsers
|
14
16
|
* Support for custom adapters
|
15
17
|
|
16
18
|
## Installation
|
17
19
|
|
18
|
-
Shale supports Ruby (MRI) 2.
|
20
|
+
Shale supports Ruby (MRI) 2.7+
|
19
21
|
|
20
22
|
Add this line to your application's Gemfile:
|
21
23
|
|
@@ -43,28 +45,32 @@ $ gem install shale
|
|
43
45
|
* [Converting object to JSON](#converting-object-to-json)
|
44
46
|
* [Converting YAML to object](#converting-yaml-to-object)
|
45
47
|
* [Converting object to YAML](#converting-object-to-yaml)
|
48
|
+
* [Converting TOML to object](#converting-toml-to-object)
|
49
|
+
* [Converting object to TOML](#converting-object-to-toml)
|
46
50
|
* [Converting Hash to object](#converting-hash-to-object)
|
47
51
|
* [Converting object to Hash](#converting-object-to-hash)
|
48
52
|
* [Converting XML to object](#converting-xml-to-object)
|
49
53
|
* [Converting object to XML](#converting-object-to-xml)
|
50
54
|
* [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
|
51
55
|
* [Mapping YAML keys to object attributes](#mapping-yaml-keys-to-object-attributes)
|
56
|
+
* [Mapping TOML keys to object attributes](#mapping-toml-keys-to-object-attributes)
|
52
57
|
* [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
|
53
58
|
* [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
|
54
59
|
* [Using XML namespaces](#using-xml-namespaces)
|
60
|
+
* [Rendering nil values](#rendering-nil-values)
|
55
61
|
* [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
56
|
-
* [
|
62
|
+
* [Additional options](#additional-options)
|
63
|
+
* [Using custom models](#using-custom-models)
|
57
64
|
* [Supported types](#supported-types)
|
58
65
|
* [Writing your own type](#writing-your-own-type)
|
59
66
|
* [Adapters](#adapters)
|
60
67
|
* [Generating JSON Schema](#generating-json-schema)
|
61
68
|
* [Compiling JSON Schema into Shale model](#compiling-json-schema-into-shale-model)
|
62
69
|
* [Generating XML Schema](#generating-xml-schema)
|
70
|
+
* [Compiling XML Schema into Shale model](#compiling-xml-schema-into-shale-model)
|
63
71
|
|
64
72
|
## Usage
|
65
73
|
|
66
|
-
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
67
|
-
|
68
74
|
### Simple use case
|
69
75
|
|
70
76
|
```ruby
|
@@ -194,6 +200,65 @@ person.to_yaml
|
|
194
200
|
# zip: E1 6AN
|
195
201
|
```
|
196
202
|
|
203
|
+
### Converting TOML to object
|
204
|
+
|
205
|
+
To use TOML with Shale you have to set adapter you want to use.
|
206
|
+
Out of the box Shale suports [Tomlib](https://github.com/kgiszczak/tomlib).
|
207
|
+
It also comes with adapter for [toml-rb](https://github.com/emancu/toml-rb) if you prefer that.
|
208
|
+
For details see [Adapters](#adapters) section.
|
209
|
+
|
210
|
+
To set it, first make sure Tomlib gem is installed:
|
211
|
+
|
212
|
+
```
|
213
|
+
$ gem install tomlib
|
214
|
+
```
|
215
|
+
|
216
|
+
then setup adapter:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
require 'tomlib'
|
220
|
+
Shale.toml_adapter = Tomlib
|
221
|
+
|
222
|
+
# Alternatively if you'd like to use toml-rb, use:
|
223
|
+
require 'shale/adapter/toml_rb'
|
224
|
+
Shale.toml_adapter = Shale::Adapter::TomlRB
|
225
|
+
```
|
226
|
+
|
227
|
+
Now you can use TOML with Shale:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
person = Person.from_toml(<<~DATA)
|
231
|
+
first_name = "John"
|
232
|
+
last_name = "Doe"
|
233
|
+
age = 50
|
234
|
+
married = false
|
235
|
+
hobbies = ["Singing", "Dancing"]
|
236
|
+
[address]
|
237
|
+
city = "London"
|
238
|
+
street = "Oxford Street"
|
239
|
+
zip = "E1 6AN"
|
240
|
+
DATA
|
241
|
+
```
|
242
|
+
|
243
|
+
### Converting object to TOML
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
person.to_toml
|
247
|
+
|
248
|
+
# =>
|
249
|
+
#
|
250
|
+
# first_name = "John"
|
251
|
+
# last_name = "Doe"
|
252
|
+
# age = 50
|
253
|
+
# married = false
|
254
|
+
# hobbies = [ "Singing", "Dancing" ]
|
255
|
+
#
|
256
|
+
# [address]
|
257
|
+
# city = "London"
|
258
|
+
# street = "Oxford Street"
|
259
|
+
# zip = "E1 6AN"
|
260
|
+
```
|
261
|
+
|
197
262
|
### Converting Hash to object
|
198
263
|
|
199
264
|
```ruby
|
@@ -230,6 +295,17 @@ person.to_hash
|
|
230
295
|
|
231
296
|
### Converting XML to object
|
232
297
|
|
298
|
+
To use XML with Shale you have to set adapter you want to use.
|
299
|
+
Shale comes with adapters for REXML, Nokogiri and OX parsers.
|
300
|
+
For details see [Adapters](#adapters) section.
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
require 'shale/adapter/rexml'
|
304
|
+
Shale.xml_adapter = Shale::Adapter::REXML
|
305
|
+
```
|
306
|
+
|
307
|
+
Now you can use XML with Shale:
|
308
|
+
|
233
309
|
```ruby
|
234
310
|
person = Person.from_xml(<<~DATA)
|
235
311
|
<person>
|
@@ -274,6 +350,8 @@ person.to_xml
|
|
274
350
|
|
275
351
|
By default keys are named the same as attributes. To use custom keys use:
|
276
352
|
|
353
|
+
:warning: **Declaring custom mapping removes default mapping for given format!**
|
354
|
+
|
277
355
|
```ruby
|
278
356
|
class Person < Shale::Mapper
|
279
357
|
attribute :first_name, Shale::Type::String
|
@@ -300,6 +378,20 @@ class Person < Shale::Mapper
|
|
300
378
|
end
|
301
379
|
```
|
302
380
|
|
381
|
+
### Mapping TOML keys to object attributes
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
class Person < Shale::Mapper
|
385
|
+
attribute :first_name, Shale::Type::String
|
386
|
+
attribute :last_name, Shale::Type::String
|
387
|
+
|
388
|
+
toml do
|
389
|
+
map 'firstName', to: :first_name
|
390
|
+
map 'lastName', to: :last_name
|
391
|
+
end
|
392
|
+
end
|
393
|
+
```
|
394
|
+
|
303
395
|
### Mapping Hash keys to object attributes
|
304
396
|
|
305
397
|
```ruby
|
@@ -370,6 +462,37 @@ DATA
|
|
370
462
|
- `map_attribute` - map element's attribute to attribute
|
371
463
|
- `map_content` - map first text node to attribute
|
372
464
|
|
465
|
+
You can use `cdata: true` option on `map_element` and `map_content` to handle CDATA nodes:
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
class Address < Shale::Mapper
|
469
|
+
attribute :content, Shale::Type::String
|
470
|
+
|
471
|
+
xml do
|
472
|
+
map_content to: :content, cdata: true
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
class Person < Shale::Mapper
|
477
|
+
attribute :first_name, Shale::Type::String
|
478
|
+
attribute :address, Address
|
479
|
+
|
480
|
+
xml do
|
481
|
+
root 'Person'
|
482
|
+
|
483
|
+
map_element 'FirstName', to: :first_name, cdata: true
|
484
|
+
map_element 'Address', to: :address
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
person = Person.from_xml(<<~DATA)
|
489
|
+
<Person>
|
490
|
+
<FirstName><![CDATA[John]]></FirstName>
|
491
|
+
<Address><![CDATA[Oxford Street]]></Address>
|
492
|
+
</person>
|
493
|
+
DATA
|
494
|
+
```
|
495
|
+
|
373
496
|
### Using XML namespaces
|
374
497
|
|
375
498
|
To map namespaced elements and attributes use `namespace` and `prefix` properties on
|
@@ -436,6 +559,52 @@ person = Person.from_xml(<<~DATA)
|
|
436
559
|
DATA
|
437
560
|
```
|
438
561
|
|
562
|
+
### Rendering nil values
|
563
|
+
|
564
|
+
By default elements with `nil` value are not rendered. You can change this behavior
|
565
|
+
by using `render_nil: true` on a mapping.
|
566
|
+
|
567
|
+
```ruby
|
568
|
+
class Person < Shale::Mapper
|
569
|
+
attribute :first_name, Shale::Type::String
|
570
|
+
attribute :last_name, Shale::Type::String
|
571
|
+
attribute :age, Shale::Type::Integer
|
572
|
+
|
573
|
+
json do
|
574
|
+
map 'first_name', to: :first_name, render_nil: true
|
575
|
+
map 'last_name', to: :last_name, render_nil: false
|
576
|
+
map 'age', to: :age, render_nil: true
|
577
|
+
end
|
578
|
+
|
579
|
+
xml do
|
580
|
+
root 'person'
|
581
|
+
|
582
|
+
map_element 'first_name', to: :first_name, render_nil: true
|
583
|
+
map_element 'last_name', to: :last_name, render_nil: false
|
584
|
+
map_attribute 'age', to: :age, render_nil: true
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
person = Person.new(first_name: nil, last_name: nil, age: nil)
|
589
|
+
|
590
|
+
puts person.to_json(pretty: true)
|
591
|
+
|
592
|
+
# =>
|
593
|
+
#
|
594
|
+
# {
|
595
|
+
# "first_name": null,
|
596
|
+
# "age": "null"
|
597
|
+
# }
|
598
|
+
|
599
|
+
puts person.to_xml(pretty: true)
|
600
|
+
|
601
|
+
# =>
|
602
|
+
#
|
603
|
+
# <person age="">
|
604
|
+
# <first_name/>
|
605
|
+
# </person>
|
606
|
+
```
|
607
|
+
|
439
608
|
### Using methods to extract and generate data
|
440
609
|
|
441
610
|
If you need full controll over extracting and generating data from/to document,
|
@@ -459,42 +628,42 @@ class Person < Shale::Mapper
|
|
459
628
|
map_element 'Address', using: { from: :address_from_xml, to: :address_to_xml }
|
460
629
|
end
|
461
630
|
|
462
|
-
def hobbies_from_json(value)
|
463
|
-
|
631
|
+
def hobbies_from_json(model, value)
|
632
|
+
model.hobbies = value.split(',').map(&:strip)
|
464
633
|
end
|
465
634
|
|
466
|
-
def hobbies_to_json
|
467
|
-
hobbies.join(', ')
|
635
|
+
def hobbies_to_json(model, doc)
|
636
|
+
doc['hobbies'] = model.hobbies.join(', ')
|
468
637
|
end
|
469
638
|
|
470
|
-
def address_from_json(value)
|
471
|
-
|
472
|
-
|
639
|
+
def address_from_json(model, value)
|
640
|
+
model.street = value['street']
|
641
|
+
model.city = value['city']
|
473
642
|
end
|
474
643
|
|
475
|
-
def address_to_json
|
476
|
-
{ 'street' => street, 'city' => city }
|
644
|
+
def address_to_json(model, doc)
|
645
|
+
doc['address'] = { 'street' => model.street, 'city' => model.city }
|
477
646
|
end
|
478
647
|
|
479
|
-
def hobbies_from_xml(value)
|
480
|
-
|
648
|
+
def hobbies_from_xml(model, value)
|
649
|
+
model.hobbies = value.split(',').map(&:strip)
|
481
650
|
end
|
482
651
|
|
483
|
-
def hobbies_to_xml(element, doc)
|
484
|
-
doc.add_attribute(element, 'hobbies', hobbies.join(', '))
|
652
|
+
def hobbies_to_xml(model, element, doc)
|
653
|
+
doc.add_attribute(element, 'hobbies', model.hobbies.join(', '))
|
485
654
|
end
|
486
655
|
|
487
|
-
def address_from_xml(node)
|
488
|
-
|
489
|
-
|
656
|
+
def address_from_xml(model, node)
|
657
|
+
model.street = node.children.find { |e| e.name == 'Street' }.text
|
658
|
+
model.city = node.children.find { |e| e.name == 'City' }.text
|
490
659
|
end
|
491
660
|
|
492
|
-
def address_to_xml(parent, doc)
|
661
|
+
def address_to_xml(model, parent, doc)
|
493
662
|
street_element = doc.create_element('Street')
|
494
|
-
doc.add_text(street_element, street.to_s)
|
663
|
+
doc.add_text(street_element, model.street.to_s)
|
495
664
|
|
496
665
|
city_element = doc.create_element('City')
|
497
|
-
doc.add_text(city_element, city.to_s)
|
666
|
+
doc.add_text(city_element, model.city.to_s)
|
498
667
|
|
499
668
|
address_element = doc.create_element('Address')
|
500
669
|
doc.add_element(address_element, street_element)
|
@@ -519,7 +688,7 @@ person = Person.from_xml(<<~DATA)
|
|
519
688
|
<Street>Oxford Street</Street>
|
520
689
|
<City>London</City>
|
521
690
|
</Address>
|
522
|
-
</
|
691
|
+
</Person>
|
523
692
|
DATA
|
524
693
|
|
525
694
|
# =>
|
@@ -530,12 +699,100 @@ DATA
|
|
530
699
|
# @city="London">
|
531
700
|
```
|
532
701
|
|
533
|
-
|
702
|
+
You can also pass a `context` object that will be available in extractor/generator methods:
|
534
703
|
|
535
|
-
|
704
|
+
```ruby
|
705
|
+
class Person < Shale::Mapper
|
706
|
+
attribute :password, Shale::Type::String
|
707
|
+
|
708
|
+
json do
|
709
|
+
map 'password', using: { from: :password_from_json, to: :password_to_json }
|
710
|
+
end
|
711
|
+
|
712
|
+
def password_from_json(model, value, context)
|
713
|
+
if context.admin?
|
714
|
+
model.password = value
|
715
|
+
else
|
716
|
+
model.password = '*****'
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
def password_to_json(model, doc, context)
|
721
|
+
if context.admin?
|
722
|
+
doc['password'] = model.password
|
723
|
+
else
|
724
|
+
doc['password'] = '*****'
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
Person.new(password: 'secret').to_json(context: current_user)
|
730
|
+
```
|
731
|
+
|
732
|
+
### Additional options
|
733
|
+
|
734
|
+
You can control which attributes to render and parse by
|
735
|
+
using `only: []` and `except: []` parameters.
|
536
736
|
|
537
737
|
```ruby
|
538
|
-
|
738
|
+
# e.g. if you have this model graph:
|
739
|
+
person = Person.new(
|
740
|
+
first_name: 'John'
|
741
|
+
last_name: 'Doe',
|
742
|
+
address: Address.new(city: 'London', street: 'Oxford Street')
|
743
|
+
)
|
744
|
+
|
745
|
+
# if you want to render only `first_name` and `address.city` do:
|
746
|
+
person.to_json(only: [:first_name, address: [:city]], pretty: true)
|
747
|
+
|
748
|
+
# =>
|
749
|
+
#
|
750
|
+
# {
|
751
|
+
# "first_name": "John",
|
752
|
+
# "address": {
|
753
|
+
# "city": "London"
|
754
|
+
# }
|
755
|
+
# }
|
756
|
+
|
757
|
+
# and if you don't need an address you can do:
|
758
|
+
person.to_json(except: [:address], pretty: true)
|
759
|
+
|
760
|
+
# =>
|
761
|
+
#
|
762
|
+
# {
|
763
|
+
# "first_name": "John",
|
764
|
+
# "last_name": "Doe"
|
765
|
+
# }
|
766
|
+
```
|
767
|
+
|
768
|
+
It works the same for parsing:
|
769
|
+
|
770
|
+
```ruby
|
771
|
+
# e.g. if you want to parse only `address.city` do:
|
772
|
+
Person.from_json(doc, only: [address: [:city]])
|
773
|
+
|
774
|
+
# =>
|
775
|
+
#
|
776
|
+
# #<Person:0x0000000113d7a488
|
777
|
+
# @first_name=nil,
|
778
|
+
# @last_name=nil,
|
779
|
+
# @address=#<Address:0x0000000113d7a140 @street=nil, @city="London">>
|
780
|
+
|
781
|
+
# and if you don't need an `address`:
|
782
|
+
Person.from_json(doc, except: [:address])
|
783
|
+
|
784
|
+
# =>
|
785
|
+
#
|
786
|
+
# #<Person:0x0000000113d7a488
|
787
|
+
# @first_name="John",
|
788
|
+
# @last_name="Doe",
|
789
|
+
# @address=nil>
|
790
|
+
```
|
791
|
+
|
792
|
+
If you need formatted output you can pass `pretty: true` parameter to `#to_json` and `#to_xml`
|
793
|
+
|
794
|
+
```ruby
|
795
|
+
person.to_json(pretty: true)
|
539
796
|
|
540
797
|
# =>
|
541
798
|
#
|
@@ -547,10 +804,10 @@ person.to_json(:pretty)
|
|
547
804
|
# }
|
548
805
|
```
|
549
806
|
|
550
|
-
You can also add an XML declaration by passing
|
807
|
+
You can also add an XML declaration by passing `declaration: true` to `#to_xml`
|
551
808
|
|
552
809
|
```ruby
|
553
|
-
person.to_xml(:
|
810
|
+
person.to_xml(pretty: true, declaration: true)
|
554
811
|
|
555
812
|
# =>
|
556
813
|
#
|
@@ -560,6 +817,67 @@ person.to_xml(:pretty, :declaration)
|
|
560
817
|
# </Person>
|
561
818
|
```
|
562
819
|
|
820
|
+
### Using custom models
|
821
|
+
|
822
|
+
By default Shale combines mapper and model into one class. If you want to use your own classes
|
823
|
+
as models you can do it by using `model` directive on the mapper:
|
824
|
+
|
825
|
+
```ruby
|
826
|
+
class Address
|
827
|
+
attr_accessor :street, :city
|
828
|
+
end
|
829
|
+
|
830
|
+
class Person
|
831
|
+
attr_accessor :first_name, :last_name, :address
|
832
|
+
end
|
833
|
+
|
834
|
+
class AddressMapper < Shale::Mapper
|
835
|
+
model Address
|
836
|
+
|
837
|
+
attribute :street, Shale::Type::String
|
838
|
+
attribute :city, Shale::Type::String
|
839
|
+
end
|
840
|
+
|
841
|
+
class PersonMapper < Shale::Mapper
|
842
|
+
model Person
|
843
|
+
|
844
|
+
attribute :first_name, Shale::Type::String
|
845
|
+
attribute :last_name, Shale::Type::String
|
846
|
+
attribute :address, AddressMapper
|
847
|
+
end
|
848
|
+
|
849
|
+
person = PersonMapper.from_json(<<~DATA)
|
850
|
+
{
|
851
|
+
"first_name": "John",
|
852
|
+
"last_name": "Doe",
|
853
|
+
"address": {
|
854
|
+
"street": "Oxford Street",
|
855
|
+
"city": "London"
|
856
|
+
}
|
857
|
+
}
|
858
|
+
DATA
|
859
|
+
|
860
|
+
# =>
|
861
|
+
#
|
862
|
+
# #<Person:0x0000000113d7a488
|
863
|
+
# @first_name="John",
|
864
|
+
# @last_name="Doe",
|
865
|
+
# @address=#<Address:0x0000000113d7a140 @street="Oxford Street", @city="London">>
|
866
|
+
|
867
|
+
PersonMapper.to_json(person, pretty: true)
|
868
|
+
|
869
|
+
# =>
|
870
|
+
#
|
871
|
+
# {
|
872
|
+
# "first_name": "John",
|
873
|
+
# "last_name": "Doe",
|
874
|
+
# "address": {
|
875
|
+
# "street": "Oxford Street",
|
876
|
+
# "city": "London"
|
877
|
+
# }
|
878
|
+
# }
|
879
|
+
```
|
880
|
+
|
563
881
|
### Supported types
|
564
882
|
|
565
883
|
Shale supports these types out of the box:
|
@@ -588,10 +906,9 @@ end
|
|
588
906
|
### Adapters
|
589
907
|
|
590
908
|
Shale uses adapters for parsing and generating documents.
|
591
|
-
By default Ruby's standard JSON
|
592
|
-
REXML for XML.
|
909
|
+
By default Ruby's standard JSON, YAML parsers are used for handling JSON and YAML documents.
|
593
910
|
|
594
|
-
You can change it by providing your own adapter. For JSON and
|
911
|
+
You can change it by providing your own adapter. For JSON, YAML and TOML, adapter must implement
|
595
912
|
`.load` and `.dump` class methods.
|
596
913
|
|
597
914
|
```ruby
|
@@ -602,6 +919,22 @@ Shale.json_adapter = MultiJson
|
|
602
919
|
Shale.yaml_adapter = MyYamlAdapter
|
603
920
|
```
|
604
921
|
|
922
|
+
To handle TOML documents you have to set TOML adapter. Out of the box `Tomlib` is supported.
|
923
|
+
Shale also provides adapter for `toml-rb` parser:
|
924
|
+
|
925
|
+
```ruby
|
926
|
+
require 'shale'
|
927
|
+
|
928
|
+
# if you want to use Tomlib
|
929
|
+
require 'tomlib'
|
930
|
+
Shale.toml_adapter = Tomlib
|
931
|
+
|
932
|
+
# if you want to use toml-rb
|
933
|
+
require 'shale/adapter/toml_rb'
|
934
|
+
Shale.toml_adapter = Shale::Adapter::TomlRB
|
935
|
+
```
|
936
|
+
|
937
|
+
To handle XML documents you have to explicitly set XML adapter.
|
605
938
|
Shale provides adapters for most popular Ruby XML parsers:
|
606
939
|
|
607
940
|
:warning: **Ox doesn't support XML namespaces**
|
@@ -609,7 +942,7 @@ Shale provides adapters for most popular Ruby XML parsers:
|
|
609
942
|
```ruby
|
610
943
|
require 'shale'
|
611
944
|
|
612
|
-
#
|
945
|
+
# if you want to use REXML:
|
613
946
|
|
614
947
|
require 'shale/adapter/rexml'
|
615
948
|
Shale.xml_adapter = Shale::Adapter::REXML
|
@@ -842,6 +1175,82 @@ end
|
|
842
1175
|
Shale::Schema::XMLGenerator.register_xml_type(MyEmailType, 'myEmailXMLType')
|
843
1176
|
```
|
844
1177
|
|
1178
|
+
### Compiling XML Schema into Shale model
|
1179
|
+
|
1180
|
+
To generate Shale data model from XML Schema use:
|
1181
|
+
|
1182
|
+
```ruby
|
1183
|
+
require 'shale/schema'
|
1184
|
+
|
1185
|
+
schema = <<~SCHEMA
|
1186
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
1187
|
+
<xs:element name="Person" type="Person" />
|
1188
|
+
|
1189
|
+
<xs:complexType name="Person">
|
1190
|
+
<xs:sequence>
|
1191
|
+
<xs:element name="FirstName" type="xs:string" />
|
1192
|
+
<xs:element name="LastName" type="xs:string" />
|
1193
|
+
<xs:element name="Address" type="Address" />
|
1194
|
+
</xs:sequence>
|
1195
|
+
</xs:complexType>
|
1196
|
+
|
1197
|
+
<xs:complexType name="Address">
|
1198
|
+
<xs:sequence>
|
1199
|
+
<xs:element name="Street" type="xs:string" />
|
1200
|
+
<xs:element name="City" type="xs:string" />
|
1201
|
+
</xs:sequence>
|
1202
|
+
</xs:complexType>
|
1203
|
+
</xs:schema>
|
1204
|
+
SCHEMA
|
1205
|
+
|
1206
|
+
Shale::Schema.from_xml([schema])
|
1207
|
+
|
1208
|
+
# =>
|
1209
|
+
#
|
1210
|
+
# {
|
1211
|
+
# "address" => "
|
1212
|
+
# require 'shale'
|
1213
|
+
#
|
1214
|
+
# class Address < Shale::Mapper
|
1215
|
+
# attribute :street, Shale::Type::String
|
1216
|
+
# attribute :city, Shale::Type::String
|
1217
|
+
#
|
1218
|
+
# xml do
|
1219
|
+
# root 'Address'
|
1220
|
+
#
|
1221
|
+
# map_element 'Street', to: :street
|
1222
|
+
# map_element 'City', to: :city
|
1223
|
+
# end
|
1224
|
+
# end
|
1225
|
+
# ",
|
1226
|
+
# "person" => "
|
1227
|
+
# require 'shale'
|
1228
|
+
#
|
1229
|
+
# require_relative 'address'
|
1230
|
+
#
|
1231
|
+
# class Person < Shale::Mapper
|
1232
|
+
# attribute :first_name, Shale::Type::String
|
1233
|
+
# attribute :last_name, Shale::Type::String
|
1234
|
+
# attribute :address, Address
|
1235
|
+
#
|
1236
|
+
# xml do
|
1237
|
+
# root 'Person'
|
1238
|
+
#
|
1239
|
+
# map_element 'FirstName', to: :first_name
|
1240
|
+
# map_element 'LastName', to: :last_name
|
1241
|
+
# map_element 'Address', to: :address
|
1242
|
+
# end
|
1243
|
+
# end
|
1244
|
+
# "
|
1245
|
+
# }
|
1246
|
+
```
|
1247
|
+
|
1248
|
+
You can also use a command line tool to do it:
|
1249
|
+
|
1250
|
+
```
|
1251
|
+
$ shaleb -c -f xml -i schema.xml
|
1252
|
+
```
|
1253
|
+
|
845
1254
|
## Contributing
|
846
1255
|
|
847
1256
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.
|