天天看點

yaml 使用自定義類型讀取和寫回檔案 .as<struce> 和emitter 以及随機出現解析失敗警告

一.意義

 yaml對于内容有類型判斷,也可以通過!!進行指定類型,是以這給讀取自定義結構類型的yaml讀取提供了可以,而不是在yaml 0.5以後的版本中還需要重載一大堆的>>符号

二.參考資料

yaml-cpp官網

https://github.com/jbeder/yaml-cpp/wiki/Tutorial

node.Type()

-Null

-Scalar

-Sequence

-map

-undefined

三.讀取

重載yaml命名空間裡面的decode函數

struct Vec3 { double x, y, z;};
           
namespace YAML {
template<>
struct convert<Vec3> {
  static bool decode(const Node& node, Vec3& rhs) {
    if(!node.IsSequence() || node.size() != 3) {
      return false;
    }
    rhs.x = node[0].as<double>();
    rhs.y = node[1].as<double>();
    rhs.z = node[2].as<double>();
    return true;
  }
};
}
           
YAML::Node node = YAML::Load("start: [1, 3, 0]");
Vec3 v = node["start"].as<Vec3>();
node["end"] = Vec3(2, -1, 0);
           

來一個複雜一點的

yaml資料為:

-name:marry
 id:10
 sex:female
 workday:[1,2,5,6]

-name:dave
 id:11
 sex:male
 workday:[2,3,4,5]

           

定義結構體為

struct employee
{
 string name;
 int id;
 string sex;
 struct workDay{double one,two,three,four};
}
           

這個時候重載decode函數為   

//decode

//其實decode函數就是告訴yaml怎麼把yaml讀取後的數組的内容和自定義資料類型進行複制
static bool decode(const Node& node, employee& subdata) {//一個"-"表示一個node

    subdata.name = node[0].as<string>();
    subdata.id = node[1].as<int>();     //這個時候類型隻能是int 否則也會解析失敗
    subdata.sex = node[2].as<double>(); //還可以在decode裡面做内容的判斷
    if(subdata.sex!="male"||subdata.sex!="female")
    return false;//函數最後return false會throw 一個exception 需要話在使用node.as<employee>的時候使用try catch去捕獲異常
    subData.workDay.one=node[3][0].as<double>();
    subData.workDay.two=node[3][1].as<double>();
    subData.workDay.three=node[3][2].as<double>();
    subData.workDay.four=node[3][3].as<double>();
    return true;
}
           

//解析失敗原因

1.通路了一個node數組為空的項

2.内容類型和指定as解析的不同,例如 id=ss這個時候就會報錯了 as支援c++八大基本類型

3.和decode函數裡面設定的條件不同 sex=man 這個時候也會throw一個exception

通過try catch 抓住yaml exception&e之後 可以通過e.what來顯示集體是哪一行出了問題

//可以用node["name"]同樣能夠讀取的到

四、寫回

yaml-cpp 0.5之後就不在使用encode這個重載函數,而是使用了一個emiter對象,是以需要在使用寫回yaml檔案的類中聲明emiter的<<為重載函數

一個emiter就相當是一個檔案對象 同樣的<<主要就是聲明怎麼把 自定義的類型資料和node建立一一對應的聯系

struct employee
{
 string name;
 int id;
 string sex;
 struct workDay{double one,two,three,four};
}
           
YAML::Emitter& operator << (YAML::Emitter& out, const employee& v) {
    <out<YAML::BeginSeq;//聲明一個檔案開始 --- 關于---和...可以參考https://blog.csdn.net/qq_34249583/article/details/81166216

    out << YAML::BeginMap
    out << YAML::Key<<"name";//name代表的是yaml檔案裡面的key名字和employee的name沒有直接關聯
    out << YAML::Value<<v.name;
    
    out <<YAML::Key<<"id";
        out <<YAML::Value<<v.id;

        out <<YAML::Key<<"sex"
        out <<YAML::Value<<v.sex;

        out <<YAML::Key<<"workDay";
        vector<double> workday<<v.workDay.one<<v.workDay.two<<v.workDay.three<<v.workDay.four;
    out <<YAML::Value<<YAML::Flow<<workday;
    out <<YAML::EndMap;

    out << YAML::EndSeq;//聲明一個檔案結束 ...
        return out;
}
           

寫完對應關系之後就是使用

YAML::Emitter emit;

emit<<employee;

file<<emit.c_str();

五、随機失敗警告

      解析字元有時候能夠解析成功,但是有時候又會随機報錯,這種情況通常來說是因為重載decode函數的時候,并不是所有路徑上都有傳回值,導緻函數return了一個随機值,這個函數一旦return false,yaml-cpp就會丢出一個異常。