Giskard

(三十七)XML编程

2018-11-01

XML

XML(EXtensible Markup Language)可扩展标记语言。本想设计为取代HTML

实际上应用较为简单,够不上语言的级别

XML是一个数据格式,比如下面

<?xml version="1.0" encoding="GBK"?>
<root>
    <host> afanihao.cn </host>
    <port> 8080  </port>
</root>

传入了两个数据,一个是域名,一个是端口号

  • 优点:可读性强
  • 缺点:解析费时,占用空间大,效率低

XML文件的格式

  • 声明

    <?xml version="1.0" encoding="GBK"?>
    
  • DTD(经常省略,用于验证)

    <!DOCTYPE...xxx.dtd...>
    
  • 内容

    <root>
        <host> afanihao.cn </host>
        <port> 8080  </port>
    </root>
    

XML文档是一个由元素构成的树形结构,root,host,port都是元素,必须配对

<name> dadada </name>
<Name> dadada </Name>

直接用浏览器打开,能打开没显示问题就是正确的

#####元素的属性

<note id = "110" page = "1">
    <time> 2018/11/11 </time>
</note>
文档结构

必须有一个唯一的根元素,名字可以随意改

处于同一级别下的元素为兄弟元素,比如就是兄弟元素

兄弟元素可以同名

<phone> 110 </phone>
<phone> 119 </phone>
形式良好的与内容合法的

语法检查没有问题,就是形式良好的。但不一定内容合法(由DTD来检验)

<?xml version = "1.0" encoding = "GBK"?>
<root>
    <host> afanihao.cn </host>
    <port> ha ha ha </port>
</root>

生成XML文档

tinyxml库:一个小巧的xml库,可以生成和解析XML文档,可以直接将源代码加载到项目中

#include "tinyxml/tinyxml.h"
//#include "..tinyxml/tinyxml.h"表示先到向上的文件夹,再进入tinyxml文件夹
//XML文件对象
TiXmlDocument xml_doc

//添加声明和元素
//方法1直接保存到文件
xml_doc.SaveFile("test.xml");

//方法2转成string
string text;
text << xml_doc;
#include <stdio.h>
#include <string>
using namespace std;

#include "tinyxml/tinyxml.h"

int test1()
{
    TiXmlDocument xml_doc;

    // 添加XML声明
    xml_doc.LinkEndChild(new TiXmlDeclaration( "1.0", "GBK", "" ));

    // 添加根元素
    TiXmlElement * xml_root = new TiXmlElement("root");
    xml_doc.LinkEndChild(xml_root);

    // 在根元素下添加其他子元素
    if(1)
    {
        // 添加host
        TiXmlElement* xml_child = new TiXmlElement("host"); 
        xml_root->LinkEndChild(xml_child );
        // 添加文档内容
        xml_child->LinkEndChild( new TiXmlText("afanihao.cn"));

        // 设置属性
        xml_child->SetAttribute("checked", "true");
        xml_child->SetAttribute("station", 1001);
    }

    if(1)
    {
        // 嵌套子元素
        TiXmlElement* xml_client = new TiXmlElement("client"); 
        xml_root->LinkEndChild(xml_client );

        TiXmlElement* xml_clientName = new TiXmlElement("name"); 
        xml_clientName->LinkEndChild( new TiXmlText("shaofa"));
        xml_client->LinkEndChild(xml_clientName);

        TiXmlElement* xml_clientId = new TiXmlElement("id"); 
        xml_clientId->LinkEndChild( new TiXmlText("200501"));
        xml_client->LinkEndChild(xml_clientId);
    }

    // 保存到文件    
    xml_doc.SaveFile("example02a.xml");

    // 或者转成string,调试状态下可见
    string text ;
    text << xml_doc;
    return 0;
}

int main()
{
    test1();
    return 0;
}
元素列表

全为一样名称的兄弟元素

<?xml version="1.0" encoding="GBK" ?>
<root>
    <NoteList>
        <Note>
            <id>1</id>
            <when>2016-1-23 10:00</when>
            <who>Wang</who>
            <what>吃饭</what>
        </Note>
        <Note>
            <id>2</id>
            <when>2016-1-21 20:00</when>
            <who>Jiang</who>
            <what>看电影</what>
        </Note>
        <Note>
            <id>3</id>
            <when>2016-1-20 9:00</when>
            <who>Li</who>
            <what>去博物馆</what>
        </Note>
    </NoteList>
</root>
#include <stdio.h>
#include <vector>
using namespace std;

#include "tinyxml/AfTinyXml.h"

struct Note
{
    int id;
    char when[32];
    char who[32];
    char what[128];
};

vector<Note> notes;

int save()
{
    TiXmlDocument xml_doc;
    xml_doc.LinkEndChild(new TiXmlDeclaration( "1.0", "GBK", "" ));

    TiXmlElement * xml_root = new TiXmlElement("root");
    xml_doc.LinkEndChild(xml_root);

    TiXmlElement* xml_NoteList = new TiXmlElement("NoteList");
    xml_root->LinkEndChild(xml_NoteList);

    // 添加host元素
    for(int i=0; i<notes.size(); i++)
    {
        Note& r = notes[i];

        TiXmlElement* xml_Note = new TiXmlElement("Note");
        xml_NoteList->LinkEndChild(xml_Note);

        AfTinyXml::addChild(xml_Note, "id", r.id);
        AfTinyXml::addChild(xml_Note, "when", r.when);
        AfTinyXml::addChild(xml_Note, "who", r.who);
        AfTinyXml::addChild(xml_Note, "what", r.what);
    }

    xml_doc.SaveFile("example02b.xml");

    return 0;
}

void addNote(int id, const char* when, const char* who, const char* what)
{
    Note n;
    n.id = id;
    strcpy(n.when, when);
    strcpy(n.who, who);
    strcpy(n.what, what);
    notes.push_back(n);
}

int main()
{
    addNote(1, "2016-1-23 10:00", "Wang", "吃饭");
    addNote(2, "2016-1-21 20:00", "Jiang", "看电影");
    addNote(3, "2016-1-20 9:00", "Li", "去博物馆");

    save();
    return 0;
}

解析XML文档

TiXmlDocument xml_doc;
xml_doc.LoadFile("test.xml");
//或者
//xml_doc.Parse("test.xml");

TiXmlElement* xml_root = xml_doc.RootElement;
#include <stdio.h>
#include "../tinyxml/tinyxml.h"

int main()
{
    // 解析xml
    TiXmlDocument xml_doc;
    if(!xml_doc.LoadFile("example02a.xml"))
    {
        return -1;
    }


    // 根节点
    TiXmlElement* xml_root = xml_doc.RootElement();
    if (NULL == xml_root)
    {
        return -1;
    }

    // 获取元素的文本与属性
    if(1)
    {
        TiXmlElement* xml_host = xml_root->FirstChildElement("host");
        const char* text = xml_host->GetText();
        const char* aChecked = xml_host->Attribute("checked");
        const char* aStation = xml_host->Attribute("station");
        printf("text:%s, check:%s, station:%s\n", text, aChecked, aStation);
    }

    //
    if(1)
    {
        TiXmlElement* xml_client = xml_root->FirstChildElement("client");

        TiXmlElement* xml_clientId = xml_client->FirstChildElement("id");
        TiXmlElement* xml_clientName = xml_client->FirstChildElement("name");

        // ...取得id和name的文本 ...
    }
    return 0;
}
同名称的元素列表
#include <stdio.h>
#include <vector>
#include <string>
using namespace std;


#include "../tinyxml/AfTinyXml.h"

struct Note
{
    int id;
    char when[32];
    char who[32];
    char what[128];
};

vector<Note> notes;

int main()
{
    // 解析xml
    TiXmlDocument xml_doc;
    if(!xml_doc.LoadFile("example02b.xml"))
    {
        return -1;
    }

    // 根节点
    TiXmlElement* xml_root = xml_doc.RootElement();
    if (NULL == xml_root)
    {
        return -1;
    }

    // 获取元素的文本与属性

    TiXmlElement* xml_NoteList = xml_root->FirstChildElement("NoteList");
    if(xml_NoteList)
    {
        TiXmlElement* xml_Note = xml_NoteList->FirstChildElement("Note");
        while(xml_Note)
        {
            // 取得子元素的文本
            int id = AfTinyXml::childAsInt(xml_Note, "id");
            string when = AfTinyXml::childAsText(xml_Note, "when");
            string who = AfTinyXml::childAsText(xml_Note, "who");            
            string what = AfTinyXml::childAsText(xml_Note, "what");

            // 保存到列表
            Note r;
            r.id = id;
            strcpy(r.when, when.c_str());
            strcpy(r.who, who.c_str());
            strcpy(r.what, what.c_str());
            notes.push_back(r);

            // 下一个兄弟元素
            xml_Note = xml_Note->NextSiblingElement("Note");
        }
    }

    return 0;
}

XML应用实例-配置文件

应用场景

  • 保存配置文件:将用户的配置保存到xml文件里
  • 服务器的RESTful接口(如亚马逊的云服务接口)
  • WebService接口

示例:一个用于Qt实现的GUI应用程序

一个实现可以修改振幅周期的图形界面程序,用户已经设置好振幅=30,周期为50,期望下次打开软件时显示的就是设置好的结果,该怎么实现?这就需要配置文件来保存用户配置。

把周期和振幅保存为xml文件,等下次软件打开时执行xml文件即可

创建Settings.h和Settings.cpp,含有load函数与save函数

///Settings.h

#ifndef _SETTINGS_H
#define _SETTINGS_H

class Settings
{
public:
    Settings();

    int load();
    int save();

public:
    int period;
    int radius;
    int grain;
};
#endif

///Settings.cpp

#include "Settings.h"
#include "../tinyxml/AfTinyXml.h"

Settings::Settings()
{

}

int Settings::load()
{
    // 解析xml
    TiXmlDocument xml_doc;
    if(!xml_doc.LoadFile("settings.xml"))
    {
        return -1;
    }

    // 根节点
    TiXmlElement* xml_root = xml_doc.RootElement();
    if (NULL == xml_root)
    {
        return -1;
    }

    this->period = AfTinyXml::childAsInt(xml_root, "period");
    this->radius = AfTinyXml::childAsInt(xml_root, "radius");
    this->grain = AfTinyXml::childAsInt(xml_root, "grain");//粒度,即线的密度
    return 0;
}

int Settings::save()
{
    TiXmlDocument xml_doc;
    xml_doc.LinkEndChild(new TiXmlDeclaration( "1.0", "GBK", "" ));

    TiXmlElement * xml_root = new TiXmlElement("root");
    xml_doc.LinkEndChild(xml_root);

    AfTinyXml::addChild(xml_root, "period", period);
    AfTinyXml::addChild(xml_root, "radius", radius);
    AfTinyXml::addChild(xml_root, "grain", grain);

    xml_doc.SaveFile("settings.xml");
    return 0;
}

照着视频自学

Tags: C/C++