2014-04-07 1 views
0

두 가지 'boost :: property_tree'-s를 고려하십시오.두 boost :: property_tree를 어떻게 다릅니 까?

ptree1 :

{ 
    "node1" : 1, 
    "node_that_only_appears_in_this_one" : 2, 
    "node3" : 
    { 
     "nested1" : 3, 
     "nested2" 
     { 
      "double_nested1" : 5, 
      "double_nested2" : "foo" 
     } 
    } 
} 

ptree2 : 나는과 같이 호출 할 수있는 일반적인 함수를 작성할 수있는 방법

{ 
    "node1" : 1, 
    "node3" : 
    { 
     "nested1" : 3, 
     "nested_that_only_appears_in_this_one" : 5, 
     "nested2" 
     { 
      "double_nested1" : 5, 
      "double_nested2" : "bar" 
     } 
    } 
} 

:

ptree_whats_changed(ptree1, ptree2); 

등, 그이 특별한 경우 반환 값은 다음과 같은 또 다른 속성 트리입니다.

{ 
    "node3" : 
    { 
     "nested_that_only_appears_in_this_one" : 5, 
     "nested2" 
     { 
      "double_nested2" : "foo" 
     } 
    } 
} 

ptree1에서 ptree2로 갈 때 노드가 제거되는지는 중요하지 않습니다. 수정되거나 추가 된 노드 만보기 만하면됩니다.

필자는 property_tree가 모든 것을 경로를 구분하는 플랫 목록으로 저장하므로 재귀 할 필요없이 쉽게 보일 수 있습니다.

답변

4

현재 버전의 Boost.Property (1.55)는 차이점 등 mathematical relations을 직접 지원하지 않습니다. 그러나 트리를 반복하는 함수를 작성하고 노드의 전체 경로와 노드 자체가있는 사용자 제공 함수를 호출 할 수 있습니다. 각 반복은 노드의 전체 경로를 제공하므로 알고리즘은 access data으로 쉽게 수행 할 수 있으며 get(), put() 또는 add()으로 결과를 구성 할 수 있습니다. 예를 들어

여기 트리 반복 할 수있는 기능이다

/// @brief Walk a Boost.PropertyTree tree, invoking the binary function with 
///  the full path and the current node. 
template <typename Tree, typename Function> 
void for_each(
    const Tree& tree, 
    Function fn, 
    const typename Tree::path_type& parent_path = typename Tree::path_type()) 
{ 
    using path_type = typename Tree::path_type; 
    for (auto&& value_pair: tree) 
    { 
    auto current_path = parent_path/path_type(value_pair.first); 
    fn(current_path, value_pair.second); 
    for_each(value_pair.second, fn, current_path); 
    } 
} 

및 알고리즘 차이 구성하는 반복 사용

/// @brief Return tree with elements in @ref s but not in @ref t. 
template <typename Tree> 
Tree tree_difference(const Tree& s, const Tree& t) 
{ 
    using data_type = typename Tree::data_type; 
    Tree result; 
    // Iterate 's', adding to the result when either a node in 
    // 't' is not present in 's' or the node's values differ. 
    for_each(s, 
    [&](const typename Tree::path_type& path, const Tree& node) 
    { 
     auto value = t.template get_optional<data_type>(path); 
     if (!value || (value.get() != node.data())) 
     result.add(path, node.data()); 
    }); 
    return result; 
} 
여기

complete example이다 그런 두 나무 사이의 차이를 보여줍니다. tree_difference()이 정확한 결과를 제공하지 못하는 경우 확장 성을 입증하기 위해 결합, 교차 및 대칭 차이와 같은 다른 작업도 추가했습니다. 다음과 같은 출력을 생성

#include <boost/property_tree/ptree.hpp> 
#include <boost/property_tree/json_parser.hpp> 
#include <boost/foreach.hpp> 
#include <iostream> 

namespace tree_ops { 

/// @brief Walk a Boost.PropertyTree tree, invoking the binary function with 
///  the full path and the current node. 
template <typename Tree, typename Function> 
void for_each(
    const Tree& tree, 
    Function fn, 
    const typename Tree::path_type& parent_path = typename Tree::path_type()) 
{ 
    using path_type = typename Tree::path_type; 
    for (auto&& value_pair: tree) 
    { 
    auto current_path = parent_path/path_type(value_pair.first); 
    fn(current_path, value_pair.second); 
    for_each(value_pair.second, fn, current_path); 
    } 
} 

/// @brief Return tree with elements in @ref s but not in @ref t. 
template <typename Tree> 
Tree tree_difference(const Tree& s, const Tree& t) 
{ 
    using data_type = typename Tree::data_type; 
    Tree result; 
    // Iterate 's', adding to the result when either a node in 
    // 't' is not present in 's' or the node's values differ. 
    for_each(s, 
    [&](const typename Tree::path_type& path, const Tree& node) 
    { 
     auto value = t.template get_optional<data_type>(path); 
     if (!value || (value.get() != node.data())) 
     result.add(path, node.data()); 
    }); 
    return result; 
} 

/// @brief Return tree with elements from both @ref s and @ref t. 
template <typename Tree> 
Tree tree_union(const Tree& s, const Tree& t) 
{ 
    // The result will always contain all values in @ref s. 
    Tree result = s; 
    // Iterate 't', add values to the result only if the node is 
    // either not in 's' or the values are different. 
    for_each(t, 
    [&](const typename Tree::path_type& path, const Tree& node) 
    { 
     auto child = s.get_child_optional(path); 
     if (!child || (child->data() != node.data())) 
     result.add(path, node.data()); 
    }); 
    return result; 
} 

/// @brief Return tree with elements common to @ref s and @ref t. 
template <typename Tree> 
Tree tree_intersection(const Tree& s, const Tree& t) 
{ 
    using data_type = typename Tree::data_type; 
    Tree result; 
    // Iterate 's', adding common elements found in 't' that have the same 
    // value. 
    for_each(s, 
    [&](const typename Tree::path_type& path, const Tree& node) 
    { 
     auto value = t.template get_optional<data_type>(path); 
     if (value && (value.get() == node.data())) 
     result.add(path, node.data()); 
    }); 
    return result; 
} 

/// @brief Return tree with elements in either @ref s or @ref t, but not 
///  both. 
template <typename Tree> 
Tree tree_symmetric_difference(const Tree& s, const Tree& t) 
{ 
    return tree_difference(tree_union(s, t), tree_intersection(s, t)); 
} 

} // namespace tree_ops 

// Expose mathematical tree operations with operators. 

/// @brief Return tree with elements in @ref lhs but not in @ref rhs. 
boost::property_tree::ptree operator-(
    const boost::property_tree::ptree& lhs, 
    const boost::property_tree::ptree& rhs) 
{ 
    return tree_ops::tree_difference(lhs, rhs); 
} 

/// @brief Return tree with elements in both @ref lhs and @ref rhs. 
boost::property_tree::ptree operator|(
    const boost::property_tree::ptree& lhs, 
    const boost::property_tree::ptree& rhs) 
{ 
    return tree_ops::tree_union(lhs, rhs); 
} 

/// @brief Return tree with elements common to @ref lhs and @ref rhs. 
boost::property_tree::ptree operator&(
    const boost::property_tree::ptree& lhs, 
    const boost::property_tree::ptree& rhs) 
{ 
    return tree_ops::tree_intersection(lhs, rhs); 
} 

/// @brief Return tree with elements in either @ref lhs or @ref rhs, but not 
///  both. 
boost::property_tree::ptree operator^(
    const boost::property_tree::ptree& lhs, 
    const boost::property_tree::ptree& rhs) 
{ 
    return tree_ops::tree_symmetric_difference(lhs, rhs); 
} 

int main() 
{ 
    std::istringstream json1_stream { 
     "{" 
     " \"node1\" : 1," 
     " \"node_that_only_appears_in_this_one\" : 2," 
     " \"node3\" :" 
     " {" 
     " \"nested1\" : 3," 
     " \"nested2\" :" 
     " {" 
     "  \"double_nested1\" : 5," 
     "  \"double_nested2\" : \"foo\"" 
     " }" 
     " }" 
     "}"}; 

    std::istringstream json2_stream { 
     "{" 
     " \"node1\" : 1," 
     " \"node3\" :" 
     " {" 
     " \"nested1\" : 3," 
     " \"nested_that_only_appears_in_this_one\" : 5," 
     " \"nested2\" :" 
     " {" 
     "  \"double_nested1\" : 5," 
     "  \"double_nested2\" : \"bar\"" 
     " }" 
     " }" 
     "}"}; 

    boost::property_tree::ptree tree1, tree2; 
    read_json(json1_stream, tree1); 
    read_json(json2_stream, tree2); 

    std::cout << "difference in tree2 and tree1:\n"; 
    write_json(std::cout, tree2 - tree1); 

    std::cout << "union of tree1 and tree2:\n"; 
    write_json(std::cout, tree1 | tree2); 

    std::cout << "intersection of tree1 and tree2:\n"; 
    write_json(std::cout, tree1 & tree2); 

    std::cout << "symmetric difference of tree1 and tree2:\n"; 
    write_json(std::cout, tree1^tree2); 
} 

:

difference in tree2 and tree1: 
{ 
    "node3": 
    { 
     "nested_that_only_appears_in_this_one": "5", 
     "nested2": 
     { 
      "double_nested2": "bar" 
     } 
    } 
} 
union of tree1 and tree2: 
{ 
    "node1": "1", 
    "node_that_only_appears_in_this_one": "2", 
    "node3": 
    { 
     "nested1": "3", 
     "nested2": 
     { 
      "double_nested1": "5", 
      "double_nested2": "foo", 
      "double_nested2": "bar" 
     }, 
     "nested_that_only_appears_in_this_one": "5" 
    } 
} 
intersection of tree1 and tree2: 
{ 
    "node1": "1", 
    "node3": 
    { 
     "nested1": "3", 
     "nested2": 
     { 
      "double_nested1": "5" 
     } 
    } 
} 
symmetric difference of tree1 and tree2: 
{ 
    "node_that_only_appears_in_this_one": "2", 
    "node3": 
    { 
     "nested2": 
     { 
      "double_nested2": "foo", 
      "double_nested2": "bar" 
     }, 
     "nested_that_only_appears_in_this_one": "5" 
    } 
} 

참고 : get_child()가 직접 또는 간접적으로 사용되는 것처럼 나무가 중복 키가있는 경우, 그 결과가 결정되지 않을 수 있습니다.

경로에 따라 각 레벨의 결과가 완전히 일치하지 않을 수도 있습니다. 즉, 동일한 키가 여러 번 나타나는 경우 선택되는 자식이 지정되지 않은 것입니다. 이 경로가있는 하위 항목이 있더라도 해결되지 않는 경로로 이어질 수 있습니다. 예 :

* a -> b -> c 
*  -> b 
* 

경로 "A.B.C", "B"의 해상도가 최초의 노드를 선택하면 성공하지만 제를 선택하는 경우 실패 할 것이다.

알고리즘 구현이 완료되면 두 개의 트리를 모두 반복하여 중복 키를 지원하는 중간 데이터 구조를 채울 필요가 있습니다. 그런 다음 중간 데이터 구조에 대해 작업을 수행하고 트리를 결과로 구성합니다.

+0

이것은 매우 친절합니다. 감사합니다! 1.64 향상과 차이점이나 차이점이 부족합니까? – Gizmo

+0

그래서 이것은 실제로'<사용자 아이디 = "2"> ADMIN<사용자 아이디 = "3"> NOTADMIN', 결과 DIFF의 변화, 어떤 생각으로 NOTADMIN를보고하는 방법이 문제를 해결하려면 실패? – Gizmo