21 Ocak 2018 Pazar

property_tree json

property_tree Sınıfı
Giriş
Şu satırı dahil ederiz.
#include <boost/property_tree/ptree.hpp>
Kolay kullanmak için şu satırı dahil ederiz.
using boost::property_tree::ptree;
Bu sınıf tam anlamıyla bir json kütüphanesi değil. Örneğin herşeyi sadece string olarak yazıyor. Açıklaması şöyle
Boost does not have an XML library.
Boost does not have a JSON library.
Boost has a Property Tree library. It deals with Property Trees. Not JSON, XML or whatever else.
Alanların tip bilgisi kaybolur. Açıklaması şöyle
[...] JSON values are mapped to nodes containing the value. However, all type information is lost; numbers, as well as the literals "null", "true" and "false" are simply mapped to their string form. Property tree nodes containing both child nodes and data cannot be mapped.
Eğer tam bir json kütüphanesi gerekiyorsa nlohmann veya RapidJSON kullanılabilir. Açıklaması şöyle
Because of these limitations, be smart and choose for a JSON library to build your requirements upon.
add_child metodu - dizi ekler
Açıklaması şöyle
Add the node at the given path. Create any missing parents. If there already is a node at the path, add another one with the same key.
Dizi olarak eklemesinin açıklaması ise şöyle. put() metodu path var ise mevcut nesneyi yeni nesne ile değiştirir. add_child ise diziye ekler.
The add_child member function allows you to insert one property_tree into the DOM of another as a child node. If the key path you provide already exists a duplicate key will be added and the child will be inserted there instead.
Bir başka açıklama şöyle
The difference between put_child() and add_child() is that put_child() accesses a key if that key already exists,while add_child() always inserts a new key into the tree.
İsmi olan bir dizi ekler. Çıktı olarak şunu alırız.
{"Some Key":[{...},{...},{...},...,{...}]}
Bu metod ile ismi olmayan bir dizi eklemek mümkün değil. Yani şu çıktıyı alamayız.
[
  {
    "childkeyA": "1",
    "childkeyB": "2"
  },
  {
    "childkeyA": "3",
    "childkeyB": "4"
  },
  {
    "childkeyA": "5",
    "childkeyB": "6"
  }
]
Açıklaması şöyle
The property tree dataset is not typed, and does not support arrays as such. Thus, the following JSON / property tree mapping is used:
  • JSON objects are mapped to nodes. Each property is a child node.
  • JSON arrays are mapped to nodes. Each element is a child node with an empty name. If a node has both named and unnamed child nodes, it cannot be mapped to a JSON representation.
  • JSON values are mapped to nodes containing the value. However, all type information is lost; numbers, as well as the literals "null", "true" and "false" are simply mapped to their string form.
  • Property tree nodes containing both child nodes and data cannot be mapped.
JSON round-trips, except for the type information loss.
Örnek
Şöyle yaparız.
boost::property_tree::propTree propTree1;

propTree2.put("l", "");
propTree2.put("m", "");
propTree1.push_back(std::make_pair("", propTree3));
propTree3.add_child("Msg", propTree1);
Çıktı olarak şunu alırız.
{
"Msg": [
    {
        "l": "",
        "m": ""
    }
]

}
Örnek
Şöyle yaparız.
ptree pt;
ptree children;
ptree child1, child2, child3;


child1.put("childkeyA", 1);
child1.put("childkeyB", 2);

child2.put("childkeyA", 3);
child2.put("childkeyB", 4);

child3.put("childkeyA", 5);
child3.put("childkeyB", 6);

children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));

pt.put("testkey", "testvalue");
pt.add_child("MyArray", children);

write_json("test2.json", pt);
Çıktı olarak şunu alırız.
{
  "testkey": "testvalue",
  "MyArray":
  [
    {
      "childkeyA": "1",
      "childkeyB": "2"
    },
    {
      "childkeyA": "3",
      "childkeyB": "4"
    },
    {
      "childkeyA": "5",
      "childkeyB": "6"
    }
  ]
}
get metodu - path
 get, get (default-value version), ve get_optional bir düğüme erişmek için kullanılabilir.
Eğer belirtilen yol yanlış ise exception atar. Şöyle yaparız.
try
{
  std::cout << "try to get not existing path" << std::endl;
  std::string path = this->m_Tree.get<std::string>(PATH);
}
catch (const boost::property_tree::ptree_bad_path& e)
{
  std::cout << "ptree_bad_path" << std::endl;
}
get metodu - path + default value
 get, get (default-value version), ve get_optional bir düğüme erişmek için kullanılabilir.
Elimizde şöyle bir json olsun
{
  "some_values":
  {
    "field_1":  "value_1",
    "field_2":  true
  }
}
Şöyle yaparız.
pTree.get<string>("some_values.field_1",  "");
pTree.get<bool>("some_values.field_2",    false);
get_child metodu
Düğümün ismi belirtilen alt düğümünün altındakileri verir. Yeni bir property_tree döner.
Örnek
Elimizde şöyle bir json olsun
"message":{
   "message_id":123,
   "from":{
      "id":12345,
      "first_name":"name",
      "username":"username"
   },
   "chat":{
      "id":12345,
      "first_name":"name",
      "username":"username",
      "type":"private"
   },
   "date":1478144459,
   "text":"this is \ud83d\udca9 a sentence"
}
text alanını değerine erişmek için şöyle yaparız.
for (const property_tree::ptree::value_type& child, root.get_child("result"))
{
  std::string message = child.second.get<std::string>("message.text");
  ...
}
Örnek
Şöyle yaparız.
std::istringstream iss(R"({
  "topology_1": {
    "clnt_id": "aldgdsgsd",
    "sensors": {
      "num_sensors": "6",
      "sensor_1": {
        "time_interval": "5#15",
        "min_bound": "",
        "max_bound": "54",
        "anomaly": "2%",
        "anomaly_window": "70",
        "jump": "10",
        "topic": "sense/thubrahali/temp",
        "qos": "1"
      }
    }
  }
})");

ptree document;
read_json(iss, document);

// and back to string
std::string topology_1 = as_json_string(document.get_child("topology_1"));
Çıktı olarak şunu alırız.
{
  "clnt_id": "aldgdsgsd",
  "sensors": {
    "num_sensors": "6",
    "sensor_1": {
       "time_interval": "5#15",
       "min_bound": "",
       "max_bound": "54",
       "anomaly": "2%",
       "anomaly_window": "70",
       "jump": "10",
       "topic": "sense\/thubrahali\/temp",
       "qos": "1"
     }
  }
}
Örnek
Elimizde şu json olsun
{  
"nodes":{  
  "192.168.1.1": {  
     "type":"type1",         
     "info":"info1",
     "error":"error1"
  },
  "192.168.1.2":{  
     "type":"type2",         
     "info":"info2",
     "error":"error2"
  },
  "test":{  
     "type":"type2",         
     "info":"info2",
     "error":"error2"
  }
 }
}
IP düğümlerini dolaşmak için şöyle yaparız.
BOOST_FOREACH(ptree::value_type &v, pt.get_child("nodes"))
{
  std::string key_ = v.first.data();
  std::string val_ = v.second.data();        
  ...
}
Örnek - get_child ile array dolaşmak
Elimizde şöyle bir json olsun
{"json": "a_string", "data": [0,1,2,3,4]}
data'nın ilk elemanına erişmek için şöyle yaparız.
string str = ...;
stringstream ss (str);
ptree root;
read_json(ss,root);

int first_value = root.get_child("data").begin()->second.get_value<int>();
Örnek - get_child ile array dolaşmak
Belirtilen indeksteki array elemanına erişmek için şöyle yaparız. element_at index'in taşıp taşmadığını kontrol etmez. element_at_checked eder.
template <typename T = std::string> 
T element_at(ptree const& pt, std::string name, size_t n) {
  return std::next(pt.get_child(name).find(""), n)->second.get_value<T>();
}

template <typename T = std::string> 
T element_at_checked(ptree const& pt, std::string name, size_t n) {
  auto r = pt.get_child(name).equal_range("");

  for (; r.first != r.second && n; --n) ++r.first;

  if (n || r.first==r.second)
    throw std::range_error("index out of bounds");

  return r.first->second.get_value<T>();
}
Şöyle yaparız.
ptree pt;
{
  std::istringstream iss("{\"a\":[1, 2, 3, 4, 5, 6]}");
  read_json(iss, pt);
}

// get the 4th element:
std::cout << element_at_checked(pt, "a", 3) << "\n";

// get it as int
std::cout << element_at_checked<int>(pt, "a", 3) << "\n";

// get non-existent array:
try {
  std::cout << element_at_checked<int>(pt, "b", 0) << "\n";
} catch(std::exception const& e) {
  std::cout << e.what() << "\n";
}

try {
  std::cout << element_at_checked<int>(pt, "a", 6) << "\n";
} catch(std::exception const& e) {
  std::cout << e.what() << "\n";
}
Örnek - get_child ile array dolaşmak
Elimizde şöyle bir json olsun
{
    "weather": [
        {
            "id": "701",
            "main": "Mist",
            "description": "brume",
            "icon": "50n"
        },
        {
            "id": "502",
            "main": "Sun",
            "description": "soleil",
            "icon": "50b"
        }
    ]
}
Şöyle yaparız.
for (auto it: root.get_child("weather")) {
  cout << it.second.get_child("description").data() << endl;
}
Çıktı olarak şunu alırız.
brume
soleil
get_child_optional metodu
Düğümler arasında ayraç olarak "/" kullanmak için şöyle yaparız.
boost::optional< ptree& > child = pt.get_child_optional(
  ptree::path_type("nodes/192.168.1.1", '/'));
get_optional metodu
 get, get (default-value version), ve get_optional bir düğüme erişmek için kullanılabilir.
Açıklaması şöyle.

Three Ways of Getting Data

There are three versions of get: get, get (default-value version), and get_optional, which differ by failure handling strategy. All versions take path specifier, which determines in which key to search for a value. It can be a single key, or a path to key, where path elements are separated with a special character (a '.' if not specified differently). For example debug.logging.errorlevel might be a valid path with dot as a separator.
Örnek
Şöyle yaparız.
ptree pt;
/* ... */
boost::optional<float> v = pt.get_optional<float>("a.path.to.float.value");
Örnek
Şöyle yaparız.
boost::property_tree::ptree tree;

// if path doesn't exist, put value
if (!tree.get_optional<bool>("my.path.to.thing").is_initialized())
{
    tree.put("my.path.to.thing", true);
}
Örnek
Elimizde şöyle bir xml olsun ve biz derindeki bir düğümü okumak isteyelim.
boost::property_tree::ptree pt = ...;
...

std::string my_value;
if (read_from(my_value, pt, "my_value")) {
  std::cout << "Retrieved value: " << my_value << "\n";
}
Şöyle yaparız.
//template <typename Ptree>
bool read_from(std::string& into, Ptree const& pt, std::string const& target) {
  if (auto v = pt.get_optional<std::string>(target)) {
    into = *v;
    return true;
  }
  for (auto& child : pt) {
    if (read_from(into, child.second, target)) return true;
  }

  return false;
}
push_back metodu
İmzası şöyle
iterator push_back(const value_type &);
Bu kütüphane diziler ile iyi çalışmıyor. İsmi olmayan bir dizi çıktısı alamıyoruz.
[{...},{...},{...},...,{...}]
Açıklaması şöyle.
The property tree dataset is not typed, and does not support arrays as such. Thus, the following JSON / property tree mapping is used:
  • JSON objects are mapped to nodes. Each property is a child node.
  • JSON arrays are mapped to nodes. Each element is a child node with an empty name. If a node has both named and unnamed child nodes, it cannot be mapped to a JSON representation.
  • JSON values are mapped to nodes containing the value. However, all type information is lost; numbers, as well as the literals "null", "true" and "false" are simply mapped to their string form.
  • Property tree nodes containing both child nodes and data cannot be mapped.
  • JSON round-trips, except for the type information loss.
Örnek
Diziye yeni nesne ekler. Şöyle yaparız.
ptree arr;
basic_ptree<std::string, std::string> elem1, elem2;

elem1.put<std::string>("key1", "pol");
elem1.put<std::string>("key2", "active");
elem2.put<std::string>("key1", "eng");
elem2.put<std::string>("key2", "available");
arr.push_back (std::make_pair("", elem1));
arr.push_back (std::make_pair("", elem2));
Bu dizi yazdırılırsa şunu alırız.
[
  {
    "key1": "pol",
    "key2": "active"
  },
  {
    "key1": "eng",
    "key2": "available"
  }
]
Örnek
Şöyle yaparız.
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

int main()
{
  boost::property_tree::ptree pt;
  std::vector<double> Vec={1.1,2.2,3.3};
  for(double x:Vec)
  {
    std::string x_string=std::to_string(x);
    pt.push_back(
      std::make_pair("", 
      boost::property_tree::ptree(x_string)) );

  }
  boost::property_tree::json_parser::write_json(std::cout, pt);
  std::cout<<std::endl;
  return 0;
}
Çıktı olarak şunu alırız. Çıktı olarak  [] almıyoruz çünkü nesnemiz 0. seviye.
{
  "": "1.100000",
  "": "2.200000",
  "": "3.300000"
}
put metodu - alan + değer ekler
json nesnesine ismi olan bir alan ve değer ekler. Alan yeni ise altta put_child metodunu çağırır. put_child + ptree olarak kullanmaktan daha kolay.
Şöyle yaparız.
basic_ptree<std::string, std::string> elem;

elem.put<std::string>("key1", "pol");
elem.put<std::string>("key2", "active");
Bu nesne yazdırılırsa şunu alırız.
{
  "key1": "pol",
  "key2": "active"
}
İsim alanı boş verilebilir. Şöyle yaparız.
ptree elem;

elem.put("", 1);
Bu nesne yazdırılırsa şunu alırız.
{
  "1"
}
put_child metodu - dizi ekler
json nesnesine ismi olan bir nesne ekler. push_back + std::make_pair kullanmaktan daha kolay.
Şöyle yaparız.
ptree arr;
...
ptree root;
root.put_child ("path1",arr);
arr bir dizi olduğu için çıktı olarak şunu alırız.
{
  "path1": [
    {
      "key1": "pol",
      "key2": "active"
    },
    {
      "key1": "eng",
      "key2": "available"
    }
  ]
}
Free Style Metodlar
read_json metodu
Şu satırır dahil ederiz.
#include<boost/property_tree/json_parser.hpp>
"boost::property_tree::json_parser" isim alanı altındaki read_json metodunu kullanırız.

Commentler
Olmasa daha iyi. Açıklaması şöyle
The official JSON standard does not define a syntax for comments.

Support for comments is implemented (or not) on a per-parser basis. It was probably something that Boost once supported for convenience but later removed for compliance (I'm speculating, as I don't use Boost myself).

If Boost no longer supports comments, you will have to strip them out before parsing.
Örnek
Şöyle yaparız.
std::stringstream ss = ...;
ptree pt;

try {
  json_parser::read_json<ptree> (ss, pt);
} catch (boost::property_tree::ptree_error &e) {
  ...
} catch (std::exception& e) {
  ...
}
Örnek - locale
Şöyle yaparız.
boost::locale::generator gen;
auto CN = gen.generate("zh_CN.GBK");
Daha sonra stream'e geçerek şöyle yaparız.
std::ifstream ifs(filename, std::ios::binary);
ifs.imbue(CN);

boost::property_tree::ptree pt;
read_json(ifs, pt); 
Örnek - exception
Daha özel bir exception yakalamak istersek ptree_error'dan kalıtan json_parser_error yakalanabilir. Şöyle yaparız.
try
{
     // Trying to load the file
}
catch (const boost::property_tree::json_parser_error& e)
{
    //Here what i do if i cant find the file
}
write_json metodu
Şöyle yaparız. Bazen include dosyalarını diğer tüm include'ların altına yazmak gerekiyor.
ptree root;
...
std::stringstream ss;
json_parser::write_json (ss, root);
Daha kolay kullanım için şöyle yaparız.
std::string as_json_string(ptree const& pt) {
  std::ostringstream oss;
  write_json(oss, pt);
  return oss.str();
}
write_json - pretty
json çıktısının daha güzel ve okunaklı olması için şöyle yaparız.
write_json(oss, pt, true);



Hiç yorum yok:

Yorum Gönder