Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
xml2json.h
1 #include <boost/optional.hpp>
2 #include <boost/property_tree/ptree.hpp>
3 #include <boost/property_tree/xml_parser.hpp>
4 #include <istream>
5 
6 #include "json/json.h"
7 
8 namespace xml2json {
9 
10 static inline void addSubArray(Json::Value &d, const std::string &key, const Json::Value &arr) {
11  if (arr.size() == 0) {
12  return;
13  } else if (arr.size() == 1) {
14  d[key] = arr[0];
15  } else {
16  d[key] = arr;
17  }
18 }
19 
20 static const int MAX_DEPTH = 10;
21 
22 static inline Json::Value treeJson(const boost::property_tree::ptree &tree, int depth = 0) {
23  namespace bpt = boost::property_tree;
24 
25  if (depth > MAX_DEPTH) {
26  throw std::runtime_error("parse error");
27  }
28 
29  bool leaf = true;
30  Json::Value output;
31 
32  struct {
33  // used to collasce same-key children into lists
34  std::string key;
35  Json::Value list = Json::Value(Json::arrayValue);
36  } cur;
37 
38  for (auto it = tree.ordered_begin(); it != tree.not_found(); it++) {
39  const std::string &val = it->first;
40  const bpt::ptree &subtree = it->second;
41  leaf = false;
42 
43  // xml attributes
44  if (val == "<xmlattr>") {
45  for (const bpt::ptree::value_type &attr : subtree) {
46  output[std::string("@") + attr.first] = attr.second.data();
47  }
48  continue;
49  }
50 
51  if (cur.key == "") {
52  cur.key = val;
53  } else if (cur.key != val) {
54  addSubArray(output, cur.key, cur.list);
55 
56  cur.key = val;
57  cur.list = Json::Value(Json::arrayValue);
58  }
59  cur.list.append(treeJson(subtree, depth + 1));
60  }
61 
62  if (cur.key != "") {
63  addSubArray(output, cur.key, cur.list);
64  }
65 
66  {
67  auto val = tree.get_value_optional<std::string>();
68  if (!!val && val.get() != "") {
69  if (leaf) {
70  // <e>c</e> -> { "e": "c" }
71  return val.get();
72  } else {
73  // <e a=b>c</e> -> { "e": { "@a": "b", "#text": "c" } }
74  output["#text"] = val.get();
75  }
76  }
77  }
78 
79  return output;
80 }
81 
82 static inline Json::Value xml2json(std::istream &is) {
83  namespace bpt = boost::property_tree;
84 
85  try {
86  bpt::ptree pt;
87  bpt::read_xml(is, pt, bpt::xml_parser::trim_whitespace);
88 
89  if (pt.size() != 1) {
90  throw std::runtime_error("parse error");
91  }
92 
93  return treeJson(pt);
94  } catch (std::exception &e) {
95  throw std::runtime_error("parse error");
96  }
97 }
98 
99 } // namespace xml2json