2009-03-04 7 views
4

J'ai réussi à analyser correctement. Mais maintenant j'ai du mal à obtenir les valeurs dont j'ai besoin. Je peux obtenir l'élément et les attributs. Mais ne peut pas obtenir les valeurs. Je voudrais obtenir la valeur du cadre dans ce xml il est 20.Obtenir des données xml à l'aide de l'analyseur xml expat

/* track the current level in the xml tree */ 
static int depth = 0; 
/* first when start element is encountered */ 
void start_element(void *data, const char *element, const char **attribute) 
{ 
int i; 

for(i = 0; i < depth; i++) 
{ 
    printf(" "); 
} 

printf("%s", element); 

for(i = 0; attribute[i]; i += 2) 
{ 
    printf(" %s= '%s'", attribute[i], attribute[i + 1]); 
} 

printf("\n"); 
depth++; 
} 

/* decrement the current level of the tree */ 
void end_element(void *data, const char *el) 
{ 
depth--; 
} 
int parse_xml(char *buff, size_t buff_size) 
{ 
    FILE *fp; 
    fp = fopen("start_indication.xml", "r"); 
    if(fp == NULL) 
    { 
    printf("Failed to open file\n"); 
    return 1; 
    } 

    XML_Parser parser = XML_ParserCreate(NULL); 
    int done; 
    XML_SetElementHandler(parser, start_element, end_element); 

    memset(buff, 0, buff_size); 
    printf("strlen(buff) before parsing: %d\n", strlen(buff)); 

    size_t file_size = 0; 
    file_size = fread(buff, sizeof(char), buff_size, fp); 

    /* parse the xml */ 
    if(XML_Parse(parser, buff, strlen(buff), XML_TRUE) == XML_STATUS_ERROR) 
    { 
     printf("Error: %s\n", XML_ErrorString(XML_GetErrorCode(parser))); 
    } 

    fclose(fp); 
    XML_ParserFree(parser); 

    return 0; 
} 



<data> 
    <header length="4"> 
      <item name="time" type="time">16</item> 
      <item name="ref" type="string">3843747</item> 
      <item name="port" type="int16">0</item> 
      <item name="frame" type="int16">20</item> 
    </header> 
</data> 

Output from parsing 


Element: data 
Element: header length= '4' 
Element: item name= 'time' type= 'time' 
Element: item name= 'ref' type= 'string' 
Element: item name= 'port' type= 'int16' 
Element: item name= 'frame' type= 'int16' 

Répondre

11

Il est assez difficile avec expat. expat est meilleur quand vous êtes seulement intéressé par la structure, pas le contenu des éléments. Pourquoi ne pas utiliser libxml à la place? Quelles sont vos raisons d'utiliser un analyseur basé sur l'égalité comme expat, plutôt que sur un arbre?

Quoi qu'il en soit, la façon de le faire est de définir un gestionnaire de données de caractères. Voici un exemple, en fonction de votre code:

#include <expat.h> 
#include <stdio.h> 
#include <string.h> 

#define BUFFER_SIZE 100000 

/* track the current level in the xml tree */ 
static int  depth = 0; 

static char *last_content; 

/* first when start element is encountered */ 
void 
start_element(void *data, const char *element, const char **attribute) 
{ 
    int    i; 

    for (i = 0; i < depth; i++) { 
     printf(" "); 
    } 

    printf("%s", element); 

    for (i = 0; attribute[i]; i += 2) { 
     printf(" %s= '%s'", attribute[i], attribute[i + 1]); 
    } 

    printf("\n"); 
    depth++; 
} 

/* decrement the current level of the tree */ 
void 
end_element(void *data, const char *el) 
{ 
    int    i; 
    for (i = 0; i < depth; i++) { 
     printf(" "); 
    } 
    printf("Content of element %s was \"%s\"\n", el, last_content); 
    depth--; 
} 

void 
handle_data(void *data, const char *content, int length) 
{ 
    char   *tmp = malloc(length); 
    strncpy(tmp, content, length); 
    tmp[length] = '\0'; 
    data = (void *) tmp; 
    last_content = tmp;   /* TODO: concatenate the text nodes? */ 
} 

int 
parse_xml(char *buff, size_t buff_size) 
{ 
    FILE   *fp; 
    fp = fopen("start_indication.xml", "r"); 
    if (fp == NULL) { 
     printf("Failed to open file\n"); 
     return 1; 
    } 

    XML_Parser  parser = XML_ParserCreate(NULL); 
    XML_SetElementHandler(parser, start_element, end_element); 
    XML_SetCharacterDataHandler(parser, handle_data); 

    memset(buff, 0, buff_size); 
    printf("strlen(buff) before parsing: %d\n", strlen(buff)); 

    size_t   file_size = 0; 
    file_size = fread(buff, sizeof(char), buff_size, fp); 

    /* parse the xml */ 
    if (XML_Parse(parser, buff, strlen(buff), XML_TRUE) == XML_STATUS_ERROR) { 
     printf("Error: %s\n", XML_ErrorString(XML_GetErrorCode(parser))); 
    } 

    fclose(fp); 
    XML_ParserFree(parser); 

    return 0; 
} 

int 
main(int argc, char **argv) 
{ 
    int    result; 
    char   buffer[BUFFER_SIZE]; 
    result = parse_xml(buffer, BUFFER_SIZE); 
    printf("Result is %i\n", result); 
    return 0; 
} 
+1

Dans la méthode "handle_data" la taille de malloc doit être la longueur + 1. – Hyndrix

+0

'handle_data' doit concaténer le texte car les données de l'élément peuvent être divisées en plusieurs appels. Citant: [http://www.xml.com/pub/a/1999/09/expat/reference.html#chardatahandler] _Un seul bloc de texte contigu sans balisage peut encore entraîner une séquence d'appels à cette gestionnaire. En d'autres termes, si vous recherchez un motif dans le texte, il peut être partagé entre les appels de ce gestionnaire. _ De même, vous n'avez pas besoin de 'data = (void *) tmp;' car vous utilisez une variable globale pour transmettre des données. – FractalSpace

11

La « valeur » 20 est les données de caractère « 20 » dans l'élément dont tagname est « élément » et dont l'attribut le nom est « frame ».

Pour recevoir des événements de données de caractères, enregistrez un rappel avec la fonction XML_SetCharacterDataHandler.

Ce rappel recevra les données de caractère. L'analyseur peut diviser les données de caractères - typiquement pour gérer la fin d'un tampon, ou pour les entités (donc pour foo&amp;bar votre gestionnaire recevra trois appels - "foo", "&" et "bar"), donc vous devez coller le les parties de chaîne ensemble à nouveau si vous avez besoin de l'ensemble des données.

Vous savez quand vous avez toutes les données de caractères dans un nœud lorsque vous recevez le rappel de début ou de fin d'élément suivant.

Lorsque vous avez toutes les données de caractères, vous pouvez le traiter.

Un exemple autonome simplifié à partir de votre code:

#include <expat.h> 
#include <stdio.h> 
#include <stdbool.h> 
#include <string.h> 

static const char* xml = 
    "<data>\n"\ 
    " <header length=\"4\">\n"\ 
    "   <item name=\"time\" type=\"time\">16</item>\n"\ 
    "   <item name=\"ref\" type=\"string\">3843747</item>\n"\ 
    "   <item name=\"port\" type=\"int16\">0</item>\n"\ 
    "   <item name=\"frame\" type=\"int16\">20</item>\n"\ 
    " </header>\n"\ 
    "</data>\n"; 

void reset_char_data_buffer(); 
void process_char_data_buffer(); 
static bool grab_next_value; 

void start_element(void *data, const char *element, const char **attribute) { 
    process_char_data_buffer(); 
    reset_char_data_buffer(); 

    if (strcmp("item", element) == 0) { 
     size_t matched = 0; 

     for (size_t i = 0; attribute[i]; i += 2) { 
      if ((strcmp("name", attribute[i]) == 0) && (strcmp("frame", attribute[i+1]) == 0)) 
       ++matched; 

      if ((strcmp("type", attribute[i]) == 0) && (strcmp("int16", attribute[i+1]) == 0)) 
       ++matched; 
     } 

     if (matched == 2) { 
      printf("this is the element you are looking for\n"); 
      grab_next_value = true; 
     } 
    } 
} 

void end_element(void *data, const char *el) { 
    process_char_data_buffer(); 
    reset_char_data_buffer(); 
} 

static char char_data_buffer[1024]; 
static size_t offs; 
static bool overflow; 

void reset_char_data_buffer (void) { 
    offs = 0; 
    overflow = false; 
    grab_next_value = false; 
} 

// pastes parts of the node together 
void char_data (void *userData, const XML_Char *s, int len) { 
    if (!overflow) { 
     if (len + offs >= sizeof(char_data_buffer)) { 
      overflow = true; 
     } else { 
      memcpy(char_data_buffer + offs, s, len); 
      offs += len; 
     } 
    } 
} 

// if the element is the one we're after, convert the character data to 
// an integer value 
void process_char_data_buffer (void) { 
    if (offs > 0) { 
     char_data_buffer[ offs ] = '\0'; 

     printf("character data: %s\n", char_data_buffer); 

     if (grab_next_value) { 
      int value = atoi(char_data_buffer); 

      printf("the value is %d\n", value); 
     } 
    } 
} 

int main (void) { 
    XML_Parser parser = XML_ParserCreate(NULL); 

    XML_SetElementHandler(parser, start_element, end_element); 
    XML_SetCharacterDataHandler(parser, char_data); 

    reset_char_data_buffer(); 

    if (XML_Parse(parser, xml, strlen(xml), XML_TRUE) == XML_STATUS_ERROR) 
     printf("Error: %s\n", XML_ErrorString(XML_GetErrorCode(parser))); 

    XML_ParserFree(parser); 

    return 0; 
} 
Questions connexes