Skip to content

Commit 985b154

Browse files
committed
Add support for merge keys
Merge keys are specified here[1] for YAML 1.1. While not part of the YAML 1.2 specification, they're very useful and are supported in other implementations[2][3][4] that target 1.2. [1]: http://yaml.org/type/merge.html [2]: https://github.com/go-yaml/yaml [3]: https://github.com/ruby/psych [4]: https://bitbucket.org/ruamel/yaml
1 parent 0fdb1b9 commit 985b154

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

include/yaml-cpp/node/detail/node.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,48 @@ class node {
116116
value.add_dependency(*this);
117117
}
118118

119+
template <typename Key>
120+
inline node* GetValueFromMergeKey(const Key& key, node* currentValue,
121+
shared_memory_holder pMemory) const {
122+
node* mergeValue =
123+
static_cast<const node_ref&>(*m_pRef).get(std::string("<<"), pMemory);
124+
if (mergeValue) {
125+
if (mergeValue->type() == NodeType::Map) {
126+
return &mergeValue->get(key, pMemory);
127+
} else if (mergeValue->type() == NodeType::Sequence) {
128+
for (const_node_iterator it = mergeValue->begin();
129+
it != mergeValue->end(); ++it) {
130+
if (it->pNode && it->pNode->type() == NodeType::Map) {
131+
node* value = it->pNode->get(key, pMemory);
132+
if (value && value->type() != NodeType::Undefined) {
133+
return value;
134+
}
135+
}
136+
}
137+
}
138+
}
139+
return currentValue;
140+
}
141+
119142
// indexing
120143
template <typename Key>
121144
node* get(const Key& key, shared_memory_holder pMemory) const {
122145
// NOTE: this returns a non-const node so that the top-level Node can wrap
123146
// it, and returns a pointer so that it can be NULL (if there is no such
124147
// key).
125-
return static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
148+
node* value = static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
149+
if (!value || value->type() == NodeType::Undefined) {
150+
return GetValueFromMergeKey(key, value, pMemory);
151+
}
152+
return value;
126153
}
127154
template <typename Key>
128155
node& get(const Key& key, shared_memory_holder pMemory) {
129156
node& value = m_pRef->get(key, pMemory);
130157
value.add_dependency(*this);
158+
if (value.type() == NodeType::Undefined) {
159+
return *GetValueFromMergeKey(key, &value, pMemory);
160+
}
131161
return value;
132162
}
133163
template <typename Key>

test/integration/load_node_test.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,32 @@ TEST(LoadNodeTest, DereferenceIteratorError) {
185185
EXPECT_THROW(node.begin()->begin()->Type(), InvalidNode);
186186
}
187187

188+
TEST(NodeTest, MergeKeyScalarSupport) {
189+
Node node = Load("{<<: {a: 1}}");
190+
ASSERT_FALSE(!node["a"]);
191+
EXPECT_EQ(1, node["a"].as<int>());
192+
}
193+
194+
TEST(NodeTest, MergeKeyExistingKey) {
195+
Node node = Load("{a: 1, <<: {a: 2}}");
196+
ASSERT_FALSE(!node["a"]);
197+
EXPECT_EQ(1, node["a"].as<int>());
198+
}
199+
200+
TEST(NodeTest, MergeKeySequenceSupport) {
201+
Node node = Load("<<: [{a: 1}, {a: 2, b: 3}]");
202+
ASSERT_FALSE(!node["a"]);
203+
ASSERT_FALSE(!node["b"]);
204+
EXPECT_EQ(1, node["a"].as<int>());
205+
EXPECT_EQ(3, node["b"].as<int>());
206+
}
207+
208+
TEST(NodeTest, NestedMergeKeys) {
209+
Node node = Load("{<<: {<<: {a: 1}}}");
210+
ASSERT_FALSE(!node["a"]);
211+
EXPECT_EQ(1, node["a"].as<int>());
212+
}
213+
188214
TEST(NodeTest, EmitEmptyNode) {
189215
Node node;
190216
Emitter emitter;

0 commit comments

Comments
 (0)