#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] ", 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, "------------------------------------- "); fprintf(stdout, "XML: %s ", 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]不存在 ", 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! "); 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] ", cur->name, pszContent); xmlFree(pszContent); } else if(cur->children->type == XML_ELEMENT_NODE) { fprintf(stdout, "NODE:[%s] ", 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] ", 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] ", 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] ", 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] ", 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文档失败 "); return -1; } xml_root = xmlDocGetRootElement(xml_doc); if(NULL == xml_root) { fprintf(stderr, "获取XML根节点失败 "); 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 ", argv[0]); return -1; } memset(filename, 0, sizeof(filename)); strcpy(filename, argv[1]); if(NULL == (fp = fopen(filename, "r"))) { fprintf(stderr, "打开文件[%s]失败 ", 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失败 "); free(data); return -1; } free(data); fprintf(stdout, "JSON: %s [%d] ", 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数据存在属性节点的的处理