shale 0.4.0 → 0.7.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.
- 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.
|