Reputation: 16724
I have the following YAML:
- PRO_PLAN:
- description: This is the Pro plan
publicName: Pro Plan
startDate: 12-20-2015
- PRO_MONTHLY_DIRECT:
- publicName: Pro Monthly
price: 20
sub_target: zone
- PRICING_COMPONENTS: &pro_entitlements
analytics_range: 21600
rules: 10
cannon: true
- PRO_ANNUAL_DIRECT:
- publicName: Pro Annual
price: 240
sub_target: zone
- PRICING_COMPONENTS:
<<: *pro_entitlements
The resulting array doesn't bring in the pro_entitlements
for the second node:
[8] pry(BF)> app_hash[0]['PRO_PLAN'][1]
=> {"PRO_MONTHLY_DIRECT"=>[{"publicName"=>"Pro Monthly", "price"=>20, "sub_target"=>"zone"}, {"PRICING_COMPONENTS"=>nil, "analytics_range"=>21600, "page_rules"=>10, "polish"=>true}]}
[9] pry(BF)> app_hash[0]['PRO_PLAN'][2]
=> {"PRO_ANNUAL_DIRECT"=>[{"publicName"=>"Pro Annual", "price"=>240, "sub_target"=>"zone"}, {"PRICING_COMPONENTS"=>nil, "<<"=>nil}]}
Upvotes: 3
Views: 1779
Reputation: 76598
I consider it a failure of the ruby YAML parser to detect an error. If you try to roundtrip this on my ruamel.yaml
parser (Python based, which would preserve the alias/anchor names and the <<<
) you get
expected a mapping or list of mappings for merging, but found scalar
in "<byte string>", line 11, column 27:
- PRICING_COMPONENTS: &pro_entitlements
^
indicating that the rest of PRICING_COMPONENTS is a scalar (null) caused by non-indentation of the key/value pairs.
You don't even need to use python or my parser for that, you can check online that the parser you are using fails to throw an error.
The cause for this is that YAML files can have empty values for mappings. And your failure to indent analytics_range: 21600
make the whole sequence element
- PRICING_COMPONENTS: &pro_entitlements
analytics_range: 21600
rules: 10
cannon: true
a single sequence element consisting of a single map, instead of a single element consisting of a map of PRICING_COMPONENTS
to a mapping of the three values beneath it. In your case pro_elements is an anchor on the null
value that is cause by the empty value for PRICING_COMPONENTS
.
So that is all "normal", although not what you wanted.
Where your parser goes wrong is on the use: <<
should throw an error on aliases that don't point to a anchor for a mapping, as the documentation specifies mappings to be merged
Upvotes: 0
Reputation: 160551
When I need to build a more complex YAML document, I generally start by using Ruby and Ruby's Hash and Array objects. The YAML serializer knows how to build aliases and anchors and will do so if we let it:
require 'yaml'
foo = {'foo' => 1}
bar = {'bar' => 2, 'foo' => foo}
baz = {'baz' => 3, 'foo' => foo}
puts [foo, bar, baz].to_yaml
# >> ---
# >> - &1
# >> foo: 1
# >> - bar: 2
# >> foo: *1
# >> - baz: 3
# >> foo: *1
Here it's creating an alias for the foo
array, then referencing it as it serializes the array of hashes.
Using the same idea for your YAML:
require 'yaml'
PRO_ENTITLEMENTS = {
'analytics_range' => 21600,
'rules' => 10,
'cannon' => true
}
doc = [
{
'PRO_PLAN' =>
[
{
'description' => 'This is the Pro plan',
'publicName' => 'Pro Plan',
'startDate' => '12-20-2015'
},
{
'PRO_MONTHLY_DIRECT' =>
[
{
'publicName' => 'Pro Monthly',
'price' => 20,
'sub_target' => 'zone'
},
{
'PRICING_COMPONENTS' => PRO_ENTITLEMENTS,
'analytics_range' => 21600,
'rules' => 10,
'cannon' => true
}
]
},
{
'PRO_ANNUAL_DIRECT' =>
[
{
'publicName' => 'Pro Annual',
'price' => 240,
'sub_target' => 'zone'
},
{
'PRICING_COMPONENTS' => PRO_ENTITLEMENTS,
}
]
}
]
}
]
puts doc.to_yaml
Running it returns:
---
- PRO_PLAN:
- description: This is the Pro plan
publicName: Pro Plan
startDate: 12-20-2015
- PRO_MONTHLY_DIRECT:
- publicName: Pro Monthly
price: 20
sub_target: zone
- PRICING_COMPONENTS: &1
analytics_range: 21600
rules: 10
cannon: true
analytics_range: 21600
rules: 10
cannon: true
- PRO_ANNUAL_DIRECT:
- publicName: Pro Annual
price: 240
sub_target: zone
- PRICING_COMPONENTS: *1
This isn't guaranteed to be the right output for your use, only an example of how to build a structure in Ruby and have YAML output it so you can see what it's supposed to look like after serializing.
We can run a round-trip test:
YAML.load(doc.to_yaml)
# => [{"PRO_PLAN"=>
# [{"description"=>"This is the Pro plan",
# "publicName"=>"Pro Plan",
# "startDate"=>"12-20-2015"},
# {"PRO_MONTHLY_DIRECT"=>
# [{"publicName"=>"Pro Monthly", "price"=>20, "sub_target"=>"zone"},
# {"PRICING_COMPONENTS"=>
# {"analytics_range"=>21600, "rules"=>10, "cannon"=>true},
# "analytics_range"=>21600,
# "rules"=>10,
# "cannon"=>true}]},
# {"PRO_ANNUAL_DIRECT"=>
# [{"publicName"=>"Pro Annual", "price"=>240, "sub_target"=>"zone"},
# {"PRICING_COMPONENTS"=>
# {"analytics_range"=>21600, "rules"=>10, "cannon"=>true}}]}]}]
Upvotes: 6
Reputation: 16507
It seems correct syntax is to add an indent to sub Hash
for PRICING_COMPONENTS
:
---
- PRO_PLAN:
- description: This is the Pro plan
publicName: Pro Plan
startDate: 12-20-2015
- PRO_MONTHLY_DIRECT:
- publicName: Pro Monthly
price: 20
sub_target: zone
- PRICING_COMPONENTS: &pro_entitlements
analytics_range: 21600
rules: 10
cannon: true
- PRO_ANNUAL_DIRECT:
- publicName: Pro Annual
price: 240
sub_target: zone
- PRICING_COMPONENTS:
<<: *pro_entitlements
Or as noted the previous man, add the Array
dash instead of just Hash
indent.
Upvotes: 1
Reputation: 10951
I'm not an YAML master but It looks like you use wrong syntax. Try this one:
-
PRO_PLAN:
-
description: "This is the Pro plan"
publicName: "Pro Plan"
startDate: 12-20-2015
-
PRO_MONTHLY_DIRECT:
-
price: 20
publicName: "Pro Monthly"
sub_target: zone
-
PRICING_COMPONENTS: &pro_entitlements
-
analytics_range: 21600
cannon: true
rules: 10
works: true
-
PRO_ANNUAL_DIRECT:
-
price: 240
publicName: "Pro Annual"
sub_target: zone
-
PRICING_COMPONENTS:
-
<<: *pro_entitlements
Upvotes: 0