著者:後藤 大地
前回までの2回で、XMLパーサーライブラリ「expat」の使い方と、expatによるXMLデータをJSONデータへ変換する方法を取り上げた。前回作成したサンプルコードはとりあえず動作するものだが、それほどきれいに整理はされていない。今回はこのサンプルコードを整理して完成させる。
記事本文掲載のシェルスクリプトマガジンvol.54は以下のリンク先でご購入できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include "xml2json.h" int main(int argc, char *argv[]) { char buf[BUFSIZ]; int len, done; XML_Parser parser = XML_ParserCreate(NULL); XML_SetElementHandler(parser, begin, end); XML_SetCharacterDataHandler(parser, chardata); do { len = fread(buf, 1, sizeof(buf), stdin); done = len < sizeof(buf); if (XML_Parse(parser, buf, (int)len, done) == XML_STATUS_ERROR) return 1; } while (!done); XML_ParserFree(parser); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
typedef enum __element_type { TYPE_STRUCTURE, TYPE_KEY, TYPE_VALUE, TYPE_NOT_APPLICABLE } ELEMENT_TYPE; typedef enum __element { ELEMENT_DOCUMENT, ELEMENT_DOCGROUP, ELEMENT_DOCINFO, ELEMENT_TITLE, ELEMENT_AUTHOR, ELEMENT_FIRSTEDITION, ELEMENT_LASTMODIFIED, ELEMENT_P, ELEMENT_NOT_APPLICABLE } ELEMENT; typedef struct __element_info { ELEMENT_TYPE element_type; ELEMENT element; char* name; } ELEMENT_INFO; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include "xml2json.h" static const ELEMENT_INFO ELEMENTS_INFO[] = { { TYPE_STRUCTURE, ELEMENT_DOCUMENT, "document" }, { TYPE_STRUCTURE, ELEMENT_DOCGROUP, "docgroup" }, { TYPE_STRUCTURE, ELEMENT_DOCINFO, "docinfo" }, { TYPE_KEY, ELEMENT_TITLE, "title" }, { TYPE_KEY, ELEMENT_AUTHOR, "author" }, { TYPE_KEY, ELEMENT_FIRSTEDITION, "firstedition" }, { TYPE_KEY, ELEMENT_LASTMODIFIED, "lastmodified" }, { TYPE_VALUE, ELEMENT_P, "p" }, { TYPE_NOT_APPLICABLE, ELEMENT_NOT_APPLICABLE, NULL } }; ELEMENT_TYPE name_2_element_type(const XML_Char *name) { int i = 0; while (ELEMENTS_INFO[i].element != ELEMENT_NOT_APPLICABLE) { if (0 == strcmp(ELEMENTS_INFO[i].name, name)) return ELEMENTS_INFO[i].element_type; i++; } return TYPE_NOT_APPLICABLE; } ELEMENT_TYPE element_2_element_type(ELEMENT element) { int i = 0; while (ELEMENTS_INFO[i].element != ELEMENT_NOT_APPLICABLE) { if (ELEMENTS_INFO[i].element == element) return ELEMENTS_INFO[i].element_type; i++; } return TYPE_NOT_APPLICABLE; } ELEMENT name_2_element(const XML_Char *name) { int i = 0; while (ELEMENTS_INFO[i].element != ELEMENT_NOT_APPLICABLE) { if (0 == strcmp(ELEMENTS_INFO[i].name, name)) return ELEMENTS_INFO[i].element; i++; } return ELEMENT_NOT_APPLICABLE; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <sys/queue.h> #include <stdlib.h> #include <malloc_np.h> #include "xml2json.h" static int initialized = 0; static struct slisthead *headp; static struct entry { ELEMENT element; SLIST_ENTRY(entry) entries; } *np; SLIST_HEAD(slisthead, entry) head = SLIST_HEAD_INITIALIZER(head); static void init(void); void stack_push(ELEMENT element) { init(); np = malloc(sizeof(struct entry)); np->element = element; SLIST_INSERT_HEAD(&head, np, entries); } void stack_pop(void) { init(); np = SLIST_FIRST(&head); SLIST_REMOVE_HEAD(&head, entries); free(np); } ELEMENT stack_top(void) { init(); np = SLIST_FIRST(&head); if (NULL == np) return ELEMENT_NOT_APPLICABLE; return (SLIST_FIRST(&head)->element); } static void init(void) { if (!initialized) { SLIST_INIT(&head); initialized = 1; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
#include "xml2json.h" static void indent(void); static int depth = 0; static ELEMENT pre_element; static ELEMENT_TYPE pre_type; static int pre_element_is_start_tag; void XMLCALL begin(void *d, const XML_Char *name, const XML_Char **attr) { ELEMENT element = name_2_element(name); ELEMENT_TYPE type = name_2_element_type(name); ELEMENT parent = stack_top(); ELEMENT_TYPE parent_type = element_2_element_type(parent); switch (element) { case ELEMENT_DOCUMENT: printf("{"); break; default: switch (type) { case TYPE_STRUCTURE: switch (element) { case ELEMENT_DOCUMENT: printf("{ "); break; default: if (pre_element_is_start_tag) printf("\n"); else printf(",\n"); break; } indent(); printf("\"%s\": {", name); break; case TYPE_KEY: switch (pre_type) { case TYPE_KEY: printf(","); break; default: break; } printf("\n"); indent(); printf("\"%s\":", name); break; case TYPE_VALUE: switch (parent_type) { case TYPE_KEY: putchar('"'); break; default: if (pre_element_is_start_tag) printf("\n"); else printf(",\n"); indent(); printf("\"content\":\""); break; } break; default: break; } break; } ++depth; pre_element = element; pre_type = type; pre_element_is_start_tag = 1; stack_push(element); } void XMLCALL end(void *d, const XML_Char * name) { --depth; ELEMENT_TYPE type = name_2_element_type(name); ELEMENT element = name_2_element(name); switch (element) { case ELEMENT_DOCUMENT: printf("\n}\n"); break; default: switch (type) { case TYPE_STRUCTURE: printf("\n"); indent(); printf("}"); break; case TYPE_KEY: break; case TYPE_VALUE: putchar('"'); break; default: break; } break; } pre_element = element; pre_type = type; pre_element_is_start_tag = 0; stack_pop(); } void chardata(void *data, const XML_Char *cdata, int len) { char *p; switch (stack_top()) { case ELEMENT_P: p = (char *)cdata; for (int i = 0; i < len; i++) if ('\n' != *p) putchar(*p++); break; default: break; } } static void indent() { for (int i = 0; i < depth; i++) printf("\t"); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include <stdio.h> #include <string.h> #include <expat.h> typedef enum __element_type { TYPE_STRUCTURE, TYPE_KEY, TYPE_VALUE, TYPE_NOT_APPLICABLE } ELEMENT_TYPE; typedef enum __element { ELEMENT_DOCUMENT, ELEMENT_DOCGROUP, ELEMENT_DOCINFO, ELEMENT_TITLE, ELEMENT_AUTHOR, ELEMENT_FIRSTEDITION, ELEMENT_LASTMODIFIED, ELEMENT_P, ELEMENT_NOT_APPLICABLE } ELEMENT; typedef struct __element_info { ELEMENT_TYPE element_type; ELEMENT element; char* name; } ELEMENT_INFO; void XMLCALL begin(void *, const XML_Char*, const XML_Char**); void XMLCALL end(void *, const XML_Char *); void XMLCALL chardata(void *, const XML_Char *, int); ELEMENT_TYPE name_2_element_type(const XML_Char *); ELEMENT_TYPE element_2_element_type(ELEMENT); ELEMENT name_2_element(const XML_Char *); void stack_push(ELEMENT); void stack_pop(void); ELEMENT stack_top(void); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
CC= cc RM= rm -f EXPAT= -lexpat LIBDIR= /usr/local/lib INCLUDEDIR= /usr/local/include CFLAGS= -I${INCLUDEDIR} -L${LIBDIR} ${EXPAT} SRCS= xml2json.c element.c process.c stack.c OUT= xml2json OBJS= ${SRCS:C,.c$,.o,} .c.o: ${CC} -I${INCLUDEDIR} -c $< build: ${OBJS} ${CC} -o ${OUT} ${CFLAGS} ${OBJS} clean: ${RM} ${OUT} ${OBJS} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="UTF-8"?> <document> <docgroup> <docinfo> <title><p>タイトル</p></title> <author><p>名前</p></author> <firstedition><p>編集開始日</p></firstedition> <lastmodified><p>編集終了日</p></lastmodified> </docinfo> <docgroup> <docinfo> <title><p>章タイトル</p></title> </docinfo> <p>説明1</p> <p>説明2</p> </docgroup> </docgroup> </document> |