void traverse(const value& tree, const std::function<void (const path&, const value&)>& func, const path& base_path, bool leafs_only ) { if (!leafs_only || tree.empty() || (tree.kind() != kind::array && tree.kind() != kind::object)) func(base_path, tree); if (tree.kind() == kind::object) { for (const auto& field : tree.as_object()) { traverse(field.second, func, base_path + field.first, leafs_only ); } } else if (tree.kind() == kind::array) { for (value::size_type idx = 0; idx < tree.size(); ++idx) traverse(tree[idx], func, base_path + idx, leafs_only ); } }
static void GenStat(Stat& stat, const value& v) { switch (v.kind()) { case kind::array: { for (value::const_array_iterator itr = v.begin_array(); itr != v.end_array(); ++itr) GenStat(stat, *itr); stat.arrayCount++; stat.elementCount += v.size(); } break; case kind::object: { for (value::const_object_iterator itr = v.begin_object(); itr != v.end_object(); ++itr) { GenStat(stat, itr->second); stat.stringLength += itr->first.size(); } stat.objectCount++; stat.memberCount += v.size(); stat.stringCount += v.size(); } break; case kind::string: stat.stringCount++; stat.stringLength += v.size(); break; case kind::integer: case kind::decimal: stat.numberCount++; break; case kind::boolean: if (v.as_boolean()) stat.trueCount++; else stat.falseCount++; break; case kind::null: stat.nullCount++; break; } }
std::pair<bool, value> delta( const object& a, const object& b, std::size_t max_size ) { array result; const std::vector<string> akeys = a.keys(); const std::vector<string> bkeys = b.keys(); std::vector<string>::const_iterator pa = akeys.begin(), pb = bkeys.begin(); for ( ; (pa != akeys.end() || pb != bkeys.end()) && result.size() < max_size; ) { if ( pb == bkeys.end() || pa != akeys.end() && *pa < *pb ) { result.add( delete_at_operation() ) .add( *pa ); ++pa; } else if ( pa == akeys.end() || pb != bkeys.end() && *pb < *pa ) { result.add( insert_at_operation() ) .add( *pb ) .add( b.at( *pb ) ); ++pb; } else { assert( *pa == *pb ); const value b_element = b.at( *pb ); std::pair<bool, value> edit_op = delta(a.at(*pa), b_element, max_size - result.size()); // use edit, if possible and shorter if ( edit_op.first && edit_op.second.size() < b_element.size() ) { result.add( edit_at_operation() ) .add( *pa ) .add( edit_op.second ); } else { result.add( update_at_operation() ) .add( *pa) .add( b_element ); } ++pa; ++pb; } } return result.size() <= max_size ? std::make_pair(true, value(result)) : std::make_pair(false, value(b)); }
virtual TContainer create(const extraction_context& context, const value& from) const override { using std::end; TContainer out; from.as_array(); // get nice error if input is not an array for (value::size_type idx = 0U; idx < from.size(); ++idx) out.insert(end(out), context.extract_sub<element_type>(from, idx)); return out; }
diff_result diff(value left, value right) { diff_result result; if (left == right) { result.same = std::move(left); } else if (left.kind() != right.kind()) { result.left = std::move(left); result.right = std::move(right); } else switch (left.kind()) { case kind::boolean: case kind::decimal: case kind::integer: case kind::null: case kind::string: result.left = std::move(left); result.right = std::move(right); break; case kind::array: result.same = array(); result.left = array(); result.right = array(); for (value::size_type idx = 0; idx < std::min(left.size(), right.size()); ++idx) { diff_result subresult = diff(std::move(left.at(idx)), std::move(right.at(idx))); result.same.push_back(std::move(subresult.same)); result.left.push_back(std::move(subresult.left)); result.right.push_back(std::move(subresult.right)); } if (left.size() > right.size()) result.left.insert(result.left.end_array(), std::make_move_iterator(left.begin_array() + right.size()), std::make_move_iterator(left.end_array()) ); else if (left.size() < right.size()) result.right.insert(result.right.end_array(), std::make_move_iterator(right.begin_array() + left.size()), std::make_move_iterator(right.end_array()) ); break; case kind::object: result.same = object(); result.left = object(); result.right = object(); for (value::object_iterator liter = left.begin_object(); liter != left.end_object(); liter = left.erase(liter) ) { auto riter = right.find(liter->first); if (riter == right.end_object()) { result.left.insert({ liter->first, std::move(liter->second) }); } else if (liter->second == riter->second) { result.same[liter->first] = std::move(liter->second); right.erase(riter); } else { diff_result subresult = diff(std::move(liter->second), std::move(riter->second)); result.left[liter->first] = std::move(subresult.left); result.right[liter->first] = std::move(subresult.right); right.erase(riter); } } for (value::object_iterator riter = right.begin_object(); riter != right.end_object(); riter = right.erase(riter) ) { result.right.insert({ riter->first, std::move(riter->second) }); } break; } return result; }