1

我正在用 cURL 检索一个用 libxml2 和 xpath 解析的 XML。结果(关键字)填充我的二维数组(myarray)。效果很好!但是我得到的 XML(基于 openweathermap.org 的指定城市的当前天气数据),取决于天气状况,有时缺少属性:例如。

如果天空晴朗,XML 就像:

<current>
<city id="4219762" name="Rome">
<coord lon="-85.164673" lat="34.257038"/>
<country>US</country>
<sun rise="2013-06-18T10:28:40" set="2013-06-19T00:55:19"/>
</city>
<temperature value="21.59" min="21" max="22" unit="celsius"/>
<humidity value="88" unit="%"/>
<pressure value="1014" unit="hPa"/>
<wind>
<speed value="1.03" name="Calm"/>
<direction value="87.001" code="E" name="East"/>
</wind>
<clouds value="75" name="broken clouds"/>
<precipitation mode="no"/>
<weather number="701" value="mist" icon="50n"/>
<lastupdate value="2013-06-18T05:35:00"/>
</current>

而如果下雨是这样的:

//same as above
<precipitation value="0.125" mode="rain" unit="3h"/>
//same as above

问题是在第一种情况下 myarray 得到 9 行:

id: 0 string: 3171168
id: 1 string: Pescara
id: 2 string: IT
id: 3 string: 24.646
id: 4 string: 92
id: 5 string: 5.7
id: 6 string: Moderate breeze
id: 7 string: no
id: 8 string: Sky is Clear

而在第二个得到 10 行:

id: 0 string: 3171168
id: 1 string: Pescara
id: 2 string: IT
id: 3 string: 24.646
id: 4 string: 92
id: 5 string: 5.7
id: 6 string: Moderate breeze
id: 7 string: 0.125
id: 8 string: rain
id: 9 string: broken clouds

所以当我在主函数中打印 myarray

printf("Cloudiness:%s",myarray[8]);

如果天空晴朗,我会得到正确的值,但如果下雨,我会得到不一致的数据(因为现在 Cloudiness 在第 9 行)。如果某些特定服务器端问题缺少其他节点/属性,则可以应用相同的行为。有没有办法为缺失的节点/属性指定一个默认值(例如 N/A),以便 myarray 获得始终一致的数据(并且行分别获得相同类型的信息)?有人有同样的问题吗?有解决方法吗?谢谢你的帮助,最好的,乔瓦尼。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>

    struct MemoryStruct {
    char *memory;
    size_t size;
};

    static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)userp;
    mem->memory = realloc(mem->memory, mem->size + realsize + 1);
    if (mem->memory == NULL) {  /* out of memory! */ 
        printf("not enough memory (realloc returned NULL)\n");
        exit(EXIT_FAILURE); }
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;
    return realsize;
}

    xmlXPathObjectPtr getnodeset (xmlDocPtr doc, xmlChar *xpath) {
    xmlXPathContextPtr context;
    xmlXPathObjectPtr result;
    context = xmlXPathNewContext(doc);
    if (context == NULL) {
        printf("Error in xmlXPathNewContext\n");
        return NULL;    }
    result = xmlXPathEvalExpression(xpath, context);
    xmlXPathFreeContext(context);
    if (result == NULL) {
        printf("Error in xmlXPathEvalExpression\n");
        return NULL;    }
    if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
        xmlXPathFreeObject(result);
        printf("No result\n");
        return NULL;    }
    return result;
}

    int xmlretrive(char* myurl, char* myxpath, char*** myarray) {
    CURL *curl_handle;
    xmlDocPtr doc;
    xmlChar *xpath = (xmlChar*) myxpath;
    xmlNodeSetPtr nodeset;
    xmlXPathObjectPtr result;
    int i;
    xmlChar *keyword;
    struct MemoryStruct chunk;
    chunk.memory = malloc(1);
    chunk.size = 0;
    curl_global_init(CURL_GLOBAL_ALL);
    curl_handle = curl_easy_init();
    curl_easy_setopt(curl_handle, CURLOPT_URL, myurl);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    curl_easy_perform(curl_handle);
    curl_easy_cleanup(curl_handle);
        //printf("%s\n", chunk.memory);
    doc = xmlParseDoc(chunk.memory);
    if (doc == NULL ) {
            fprintf(stderr,"Document not parsed successfully. \n");
            return NULL;    }
    result = getnodeset (doc, xpath);
    if (result) {
        nodeset = result->nodesetval;
        *myarray = malloc((nodeset->nodeNr + 1) * sizeof(*myarray));
        for (i=0; i < nodeset->nodeNr; i++) {
            keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->xmlChildrenNode, 1);
            (*myarray)[i] = malloc(strlen(keyword)+1);
            if ((*myarray)[i] == NULL) {
            // out of memory.  print error msg then exit
            }
            strcpy((*myarray)[i], keyword);
            xmlFree(keyword);
        }
        xmlXPathFreeObject (result);
    }
    xmlFreeDoc(doc);
    xmlCleanupParser();
    if(chunk.memory)
    free(chunk.memory);
    curl_global_cleanup();
    return i-1;
}

    int main(void)  {
char thisxpath[300];
char thisurl[200];
char** myarray = NULL;
char output[900] = "";
int arr_rows;
strcpy (thisurl,"http://api.openweathermap.org/data/2.5/weather?q=Rome&mode=xml&units=metric");
strcpy (thisxpath,"//city/@*[name()='name' or name()='id'] | //country | //weather/@value | //temperature/@value | //precipitation/@*[name()='value' or name()='mode'] | //humidity/@value | //speed/@*[name()='name' or name()='value']");
arr_rows = xmlretrive (thisurl, thisxpath, &myarray);

    // for cycle to print myarray with some great layout

free(myarray);
return 0;
}
4

1 回答 1

1

如果您使用的是 XPath 2.0,则可以使用以下构造:

(/precipitation/@value, "NA")[1]

但是,由于您使用的是 libxml2,我相信您会被 XPath 1 困住,这在其中不起作用。

无论哪种情况,我都建议采用不同的方法。与其使用一个大而复杂的 XPath 来选择一堆节点,然后假设您关心的节点都将按正确的顺序出现一次,而是将逻辑放入 c 代码中。循环遍历您感兴趣的节点,并getnodeset()使用特定的 XPath 对每个节点进行调用。然后,您可以调查返回值以确定该值是否存在,并将该值或某个占位符插入到所需位置的数组中。

我这样做的方法是将各个 XPath 表达式放入一个数组并循环它;然后只需使用 XPath 数组中的索引作为输出数组中的索引;这样,输出值总是在一个可预测的位置,但 c 代码不需要知道任何关于实际 XPath 的信息,或者什么值在哪里。

于 2013-06-19T09:44:31.647 回答