簡體   English   中英

如何在不知道映射中的鍵和終端標量類型的情況下使用 yaml-cpp 庫解析任意 yaml 文件?

[英]How to parse arbitrary yaml file with yaml-cpp library without knowing keys in mappings and types of the terminal scalars?

最近,我的任務是解析包含一些自動計算參數的 YAML 文件。 我之前沒有聽說過“YAML”這個詞,但老實說,我可以在互聯網上找到所有關於它的內容。

我看到的所有解析示例都是基於他們事先知道解析的 .yaml 文件的內容這一事實。 他們知道 yaml 映射中的鍵和終端標量的類型。 但是對於我必須編寫的組件,情況並非如此。 該組件是一種中介,它只應將 .yaml 文件轉換為復合運行時結構,作為最終客戶端的數據源。

選擇運行時表示的“復合”模式是因為 YAML 文本本身是一種“持久復合”。 此運行時結構的草稿可能如下所示:

class CAnyAttribute{};

class CAttrBaseNode
{
    std::wstring    m_Name;
    std::string     m_Tag;
public:
    CAttrBaseNode(const std::wstring& Name) : m_Name(Name) {}

    std::wstring Name() const { return m_Name; }
    std::string Tag() const { return m_Tag; }
    void SetTag(const std::string& TagVal) { m_Tag = TagVal; }

    virtual ~CAttrBaseNode() {}

    virtual bool IsScalar() const = 0;

    virtual CAnyAttribute Value() const
        { return CAnyAttribute(); }
    virtual bool SetValue(const CAnyAttribute& Val)
        { return false; }

    virtual CAttrBaseNode* Child(int Idx) const
        { return 0; }       // Get node by index
    virtual CAttrBaseNode* Child(const std::wstring& Key) const
        { return 0; }       // Get node by key

    virtual bool AddChild(CAttrBaseNode* pNode, int Idx = -1)
        { return false; }   // Add node by index
};

class CAttrLeafNode : public CAttrBaseNode
{
    CAnyAttribute   m_Value;
public:
    CAttrLeafNode(const std::wstring& Name, const CAnyAttribute& Val) :
        CAttrBaseNode(Name), m_Value(Val) {}
    ~CAttrLeafNode() {}

    bool IsScalar() const override { return true; }

    CAnyAttribute Value() const override
        { return m_Value; }
    bool SetValue(const CAnyAttribute& Val) override
        { m_Value = Val; return true; }
};

class CAttrCompositeNode : public CAttrBaseNode
{
    std::vector<CAttrBaseNode*>     m_Children;
public:
    CAttrCompositeNode(const std::wstring& Name) : CAttrBaseNode(Name) {}
    ~CAttrCompositeNode() { for (auto& pChild : m_Children) delete pChild; }

    bool IsScalar() const override { return false; }

    CAttrBaseNode* Child(int Idx) const override
        {
            return (0 <= Idx && Idx < (int)m_Children.size()) ? m_Children[Idx] : 0;
        }
    CAttrBaseNode* Child(const std::wstring& Key) const override
        {
            auto it = std::find_if(m_Children.begin(), m_Children.end(),
                    [Key](CAttrBaseNode* pNode)->bool
                    { return pNode->Name() == Key; });
            return (it != m_Children.end()) ? *it : 0;
        }

    bool AddChild(CAttrBaseNode* pNode, int Idx = -1) override
        {
            if (pNode == 0 || Idx >= (int)m_Children.size())
                return false;
            if (Idx < 0)
                m_Children.push_back(pNode);
            else
                m_Children.insert(m_Children.begin() + Idx, pNode);
            return true;
        }
};

在這個草案中, CAnyAttribute是一個具體的類,可以分配任何標量值:不同大小的整數和浮點數、字符串甚至原始“N 字節”。 此類已在我們的軟件產品中使用多年。

我知道整個任務可能看起來很奇怪,因為來自 yaml-cpp 解析器的 YAML::Node 本身就是 YAML 文本的運行時表示。 有兩個原因。 首先,YAML::Node 接口不是標准的,沒有很好的文檔記錄並且需要時間來理解。 假設公司里有很多程序員都要處理,這是很多時間。 第二點也是更重要的一點,我們不想與一個特定的庫緊密聯系在一起。

現在的問題是:如何將任意一個 yaml 文件解析為上述運行時結構? 我們使用最新版本 0.6 的 yaml-cpp 庫。

我找到了一個解決方案,我希望它對其他人也有用。 這里是:

void Scalar2AnyAttribute(const YAML::Node& ScalarNode, CAnyAttribute& AnyAttr)
{
    assert(ScalarNode.IsScalar());
    //
    // Parse the scalar value using the @flyx's advice.
    //
}

CAttrBaseNode* Translate(const YAML::Node& YNode, const std::string& Name = std::string())
{
    CAttrBaseNode* pRet = 0;

    switch (YNode.Type())
    {
    case YAML::NodeType::Null:
        pRet = new CAttrLeafNode(Name, CAnyAttribute());
        break;
    case YAML::NodeType::Scalar:
        {
            CAnyAttribute Value;
            Scalar2AnyAttribute(YNode, Value);
            pRet = new CAttrLeafNode(Name, Value);
        }
        break;
    case YAML::NodeType::Sequence:
        {
            CAttrCompositeNode* pComp = new CAttrCompositeNode(Name);
            for (YAML::const_iterator it = YNode.begin(); it != YNode.end(); ++it)
                pComp->AddChild(Translate(*it));
            pRet = pComp;
        }
        break;
    case YAML::NodeType::Map:
        {
            CAttrCompositeNode* pComp = new CAttrCompositeNode(Name);
            for (YAML::const_iterator it = YNode.begin(); it != YNode.end(); ++it)
            {
                std::string MappingKey = it->first.as<std::string>();
                pComp->AddChild(Translate(it->second, MappingKey));
            }
            pRet = pComp;
        }
        break;
    default:
        break;
    }

    if (pRet)
        pRet->SetTag(YNode.Tag());

    return pRet;
}

int main()
{
    std::string file("....\\a.yaml");
    YAML::Node baseNode = YAML::LoadFile(file);
    CAttrBaseNode* pComposite = Translate(baseNode);
    // ...
    // Work with pComposite.
    // ...
    delete pComposite;
    std::cout << "Hello, my first translation from YAML!\n";
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM