#include <stdlib.h> #include <string.h> #include <stdio.h> #include <sys/stat.h> #include <iconv.h> #include <unistd.h> #include <errno.h> #include <libxml/xmlmemory.h> #include <libxml/parser.h> #include "cJSON.h" /* 編碼轉換函數 1、from_charset 原始編碼格式 2、to_charset 轉換編碼格式 3、outlen 轉換后的數據長度,注意:要賦初始值 */ static int string_conv(const char* from_charset, const char* to_charset, char* inbuf, size_t inlen, char* outbuf, size_t* outlen) { iconv_t cd; size_t iResult; int iRet ; char** pin = &inbuf; char** pout = &outbuf; cd = iconv_open(to_charset, from_charset); if (cd < 0) { //轉換失敗,看看原因是什么 fprintf(stderr, "conv code error : %s[%d]\n", strerror(errno), errno); return -1; } memset(outbuf, 0, *outlen); iResult = iconv(cd, pin, &inlen, pout, outlen); iconv_close(cd); return iResult; } /* 輸出XML數據 1、data XML數據 2、len 數據長度 */ void print_xml(const char* data, size_t len) { xmlDocPtr doc; xmlBufferPtr buff; xmlChar *dump; int size; buff = xmlBufferCreate(); doc = xmlReadMemory(data, len, "", "", 0); xmlDocDumpFormatMemory( doc, &dump, &size, 1 ); xmlBufferEmpty( buff ); xmlBufferAdd( buff, dump, size ); xmlFree( dump ); fprintf(stdout, "-------------------------------------\n"); fprintf(stdout, "XML:\n%s\n", xmlBufferContent(buff)); xmlBufferFree(buff); xmlFreeDoc(doc); return ; } /* 從文件中獲取XMLDOC */ xmlDocPtr parseDoc(const char* docname) { xmlDocPtr doc; FILE *fp = NULL; struct stat st; char *data = NULL; char szLine[1024]; // 從文件中讀取數據 if(NULL == (fp = fopen(docname, "r"))) { fprintf(stderr, "文件[%s]不存在\n", docname); return NULL; } memset(&st, 0, sizeof(st)); stat(docname, &st); data = (char*)malloc(st.st_size); memset(data, 0, st.st_size); memset(szLine, 0, sizeof(szLine)); while(fgets(szLine, sizeof(szLine), fp)) { strncpy(data + strlen(data), szLine, strlen(szLine)); memset(szLine, 0, sizeof(szLine)); } fclose(fp); // 采用讀取數據的方式來解析XML xmlKeepBlanksDefault(0); /* 直接解析文件XML doc = xmlParseFile(docname); */ doc = xmlReadMemory((char*)data, st.st_size, "", "gbk", 1); if(NULL == doc) { fprintf(stderr, "Document parsed failed!\n"); return NULL; } print_xml(data, strlen(data)); free(data); return doc; } /* 遞歸輸出XML所有節點信息 */ void printXmlChildren(xmlNodePtr xml_root) { xmlNodePtr cur = NULL; xmlChar *pszContent; for(cur = xml_root; cur; cur = cur->next) { if(cur->type == XML_ELEMENT_NODE) { if(cur->children->type == XML_TEXT_NODE) { pszContent = xmlNodeGetContent(cur); fprintf(stdout, "NODE:[%s][%s]\n", cur->name, pszContent); xmlFree(pszContent); } else if(cur->children->type == XML_ELEMENT_NODE) { fprintf(stdout, "NODE:[%s]\n", cur->name); printXmlChildren(cur->children); } } } return ; } /* XML數據轉換成JSON數據 */ void _xml_json(xmlNodePtr xml_node, cJSON** json_node) { xmlNodePtr xml_cur = NULL, xml_loop = NULL, xml_tmp = NULL; xmlAttrPtr xml_attr = NULL; cJSON *json_cur = NULL, *json_array = NULL; char szTmp[1024], szName[1025], szValue[1024]; xmlChar *pszContent = NULL; size_t len = 0; for(xml_cur = xml_node; xml_cur; xml_cur = xml_cur->next) { if(xml_cur->type == XML_ELEMENT_NODE) { // fprintf(stdout, "cur:[%s]\n", xml_cur->name); // 下級節點是內容 if(xml_cur->children->type == XML_TEXT_NODE) { // 判斷同級節點是否存在項目名稱的節點數據,如果存在,則為循環節點 xml_loop = xml_cur->next; while(xml_loop) { xml_tmp = xml_loop->next; if(0 == xmlStrcmp(xml_loop->name, xml_cur->name)) { // fprintf(stdout, "--->loop:[%s]\n", xml_loop->name); if(NULL == json_array) json_array = cJSON_CreateArray(); // XML內容編碼格式為UTF,需要轉換成GBK xml_attr = xml_loop->properties; // 無屬性節點直接設置為string if(NULL == xml_attr) { memset(szValue, 0, sizeof(szValue)); pszContent = xmlNodeGetContent(xml_loop); len = strlen(pszContent); string_conv("UTF-8", "GBK", pszContent, strlen(pszContent), szValue, &len); xmlFree(pszContent); json_cur = cJSON_CreateString(szValue); } // 有屬性節點,需要創建節點 else { // 屬性節點的值放在"#text"中 memset(szName, 0, sizeof(szName)); strcpy(szName, "#text"); json_cur = cJSON_CreateObject(); memset(szValue, 0, sizeof(szValue)); pszContent = xmlNodeGetContent(xml_loop); len = strlen(pszContent); string_conv("UTF-8", "GBK", pszContent, strlen(pszContent), szValue, &len); xmlFree(pszContent); cJSON_AddStringToObject(json_cur, szName, szValue); while(xml_attr) { // 其余屬性節點添加"@" memset(szName, 0, sizeof(szName)); sprintf(szName, "@%s", xml_attr->name); memset(szValue, 0, sizeof(szValue)); string_conv("UTF-8", "GBK", xml_attr->children->content, strlen(xml_attr->children->content), szValue, &len); cJSON_AddStringToObject(json_cur, szName, szValue); xml_attr = xml_attr->next; } } cJSON_AddItemToArray(json_array, json_cur); xmlUnlinkNode(xml_loop); // 處理完成后刪除該節點,要不然FOR循環會再處理一次 xmlFreeNode(xml_loop); } xml_loop = xml_tmp; } xml_attr = xml_cur->properties; // 無屬性節點直接設置成string if(NULL == xml_attr) { memset(szValue, 0, sizeof(szValue)); pszContent = xmlNodeGetContent(xml_cur); len = strlen(pszContent); // XML內容編碼格式為UTF,需要轉換成GBK string_conv("UTF-8", "GBK", pszContent, strlen(pszContent), szValue, &len); xmlFree(pszContent); json_cur = cJSON_CreateString(szValue); } // 有屬性需要創建節點,並轉換xml屬性的數據 else { json_cur = cJSON_CreateObject(); memset(szName, 0, sizeof(szName)); strcpy(szName, "#text"); memset(szValue, 0, sizeof(szValue)); pszContent = xmlNodeGetContent(xml_cur); len = strlen(pszContent); string_conv("UTF-8", "GBK", pszContent, strlen(pszContent), szValue, &len); xmlFree(pszContent); cJSON_AddStringToObject(json_cur, szName, szValue); while(xml_attr) { memset(szName, 0, sizeof(szName)); snprintf(szName, sizeof(szName), "@%s", xml_attr->name); memset(szValue, 0, sizeof(szValue)); len = strlen(xml_attr->children->content); string_conv("UTF-8", "GBK", xml_attr->children->content, strlen(xml_attr->children->content), szValue, &len); cJSON_AddStringToObject(json_cur, szName, szValue); xml_attr = xml_attr->next; } } // 判斷是否是循環數據,如果是的插入到array再存放到數據中 if(NULL != json_array) { cJSON_AddItemToArray(json_array, json_cur); cJSON_AddItemToObject(*json_node, xml_cur->name, json_array); json_array = NULL; } else cJSON_AddItemToObject(*json_node, xml_cur->name, json_cur); } // 下級節點也是節點 else if(xml_cur->children->type == XML_ELEMENT_NODE) { // 判斷同級節點是否存在項目名稱的節點數據,如果存在,則為循環節點 xml_loop = xml_cur->next; while(xml_loop) { xml_tmp = xml_loop->next; if(0 == xmlStrcmp(xml_loop->name, xml_cur->name)) { // fprintf(stdout, "--->loop:[%s]\n", xml_loop->name); if(NULL == json_array) json_array = cJSON_CreateArray(); json_cur = cJSON_CreateObject(); xml_attr = xml_loop->properties; while(xml_attr) { memset(szName, 0, sizeof(szName)); snprintf(szName, sizeof(szName) - 1, "@%s", xml_attr->name); memset(szValue, 0, sizeof(szValue)); len = strlen(xml_attr->children->content); string_conv("UTF-8", "GBK", xml_attr->children->content, strlen(xml_attr->children->content), szValue, &len); cJSON_AddStringToObject(json_cur, szName, szValue); xml_attr=xml_attr->next; } // 使用遞歸的方法轉換該節點數據 _xml_json(xml_loop->children, &json_cur); cJSON_AddItemToArray(json_array, json_cur); xmlUnlinkNode(xml_loop); // 處理完成的節點需要刪除,要不然FOR循環會再處理一次 xmlFreeNode(xml_loop); } xml_loop = xml_tmp; } json_cur = cJSON_CreateObject(); xml_attr = xml_cur->properties; while(xml_attr) { memset(szName, 0, sizeof(szName)); snprintf(szName, sizeof(szName) - 1, "@%s", xml_attr->name); //fprintf(stdout, "name:[%s]\n", szName); memset(szValue, 0, sizeof(szValue)); len = strlen(xml_attr->children->content); string_conv("UTF-8", "GBK", xml_attr->children->content, strlen(xml_attr->children->content), szValue, &len); cJSON_AddStringToObject(json_cur, szName, szValue); xml_attr = xml_attr->next; } _xml_json(xml_cur->children, &json_cur); // 判斷是否是循環數據,如果是的插入到array再存放到數據中 if(NULL != json_array) { cJSON_AddItemToArray(json_array, json_cur); cJSON_AddItemToObject(*json_node, xml_cur->name, json_array); json_array = NULL; } else cJSON_AddItemToObject(*json_node, xml_cur->name, json_cur); } } } return ; } // 注意,參數json為返回數據,需要釋放 long xml2json(const char* xml, const long xml_len, char** json, long* json_len) { xmlBufferPtr buff; xmlDocPtr xml_doc; xmlNodePtr xml_root; cJSON *json_root; char *p = NULL; xmlKeepBlanksDefault(0); buff = xmlBufferCreate(); xml_doc = xmlReadMemory(xml, xml_len, "", "gbk", 0); if(NULL == xml_doc) { fprintf(stderr, "解析XML文檔失敗\n"); return -1; } xml_root = xmlDocGetRootElement(xml_doc); if(NULL == xml_root) { fprintf(stderr, "獲取XML根節點失敗\n"); xmlFree(xml_doc); return -1; } json_root = cJSON_CreateObject(); _xml_json(xml_root, &json_root); *json = cJSON_Print(json_root); *json_len = strlen(*json); cJSON_Delete(json_root); xmlFreeDoc(xml_doc); xmlBufferFree(buff); xmlCleanupParser(); return 0; } int main(int argc, char* argv[]) { char filename[1024]; struct stat st; FILE *fp = NULL; char line[1024], *data = NULL, *json=NULL; long len; if(1 >= argc) { fprintf(stderr, "Usage:[%s] xmlfilename\n", argv[0]); return -1; } memset(filename, 0, sizeof(filename)); strcpy(filename, argv[1]); if(NULL == (fp = fopen(filename, "r"))) { fprintf(stderr, "打開文件[%s]失敗\n", filename); return -1; } memset(&st, 0, sizeof(st)); stat(filename, &st); data = (char*)malloc(st.st_size + 1); memset(line, 0, sizeof(line)); while(fgets(line, sizeof(line) - 1, fp)) { strncpy(data + strlen(data), line, strlen(line)); memset(line, 0, sizeof(line)); } fclose(fp); if(0 != xml2json(data, strlen(data), &json, &len)) { fprintf(stderr, "XML轉JSON失敗\n"); free(data); return -1; } free(data); fprintf(stdout, "JSON:\n%s\n[%d]\n", json, len); free(json); return 0; }
問題記錄:
1、數據編碼問題,libxml2內部編碼格式是UTF-8,使用xmlNodeGetContent()方法獲取的數據需要轉換成GBK編碼格式,本文采用conv庫轉換編碼格式
2、內存釋放問題
1)、xmlNodeGetContent()方法獲取的數據需要釋放
2)、xmlUnlinkNode()方法刪除節點后需要將使用xmlFreeNode()方法釋放內存
3)、JSON數據可以使用cJSON_Delete()方法一次釋放
4)、cJSON_Print()方法返回的字符串數據需要釋放
3、XML數據存在循環結構的處理
4、XML數據存在屬性節點的的處理
