XML Parser

From Nutwiki
Jump to: navigation, search

Description

This example demonstrates how to load and parse an XML file from uROM. The example can easily be adapted to read different XML files or to read the file from a different location.

For the example to work, you'll need a sub-directory called data, containing the example.xml

Source Code

<source lang="c">

  1. include <dev/board.h>
  2. include <dev/urom.h>
  3. include <fs/uromfs.h>
  4. include <stdio.h>
  5. include <io.h>

/* This is the XML library */

  1. include <pro/uxml.h>

/*

    Include our uROM file
  • /
  1. include "urom_data.c"

static UXML_NODE *products_tree;

int main(void) {

   /* This filepointer holds the XML stream */
   FILE *stream;
   /* These are the tags we want to parse. Unspecified tags are NOT parsed to save resources. */
   char *f_tags[] = { "board", NULL };
   /* These are the attributes we want to parse. Unspecified attributes are NOT parsed to save resources. */
   char *f_attribs[] = { "name", "description", "mmc", NULL };
   /* Temporary node pointers. We'll use them to iterate through our XML tree later on. */
   UXML_NODE *node;
   UXML_ATTRIB *attr;
   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("XML reading example\n");
   if (NutRegisterDevice(&devUrom, 0, 0)) {
       printf("UROM error\n");
   }
   stream = fopen("UROM:example.xml", "r");
   products_tree = UxmlParseStream(stream, f_tags, f_attribs);
   node = products_tree;
   while (node) {
       puts("\n");
       attr = node->xmln_attribs;
       while (attr) {
           printf("\t%s: \t%s\n", attr->xmla_name, attr->xmla_value);
           attr = attr->xmla_next;
       }
       node = node->xmln_next;
   }
   UxmlTreeDestroy(products_tree);
   fclose(stream);
   for (;;);

} </source>

You'll also need a slightly altered Makefile that creates your urom_data.c for you.

<source lang="c"> PROJ = xml_ex DATADIR = data DATAFILE = urom_data.c

include ../Makedefs

SRCS = $(PROJ).c OBJS = $(SRCS:.c=.o) LIBS = $(LIBDIR)/nutinit.o -lnutpro -lnutos -lnutnet -lnutfs -lnutcrt -lnutdev -lnutarch TARG = $(PROJ).hex

all: $(DATAFILE) $(OBJS) $(TARG) $(ITARG) $(DTARG)

$(DATAFILE): $(DATADIR)/example.xml $(CRUROM) -r -o$(DATAFILE) $(DATADIR)

include ../Makerules

clean: -rm -f $(OBJS) -rm -f $(TARG) $(ITARG) $(DTARG) -rm -f $(DATAFILE) </source>

The example.xml file should reside in the data subdirectory of your source directory. To achieve the test output, this XML file was used: <source lang="xml"> <?xml version="1.0"?> <catalog> <board id="enut_1.3" name="Ethernut 1.3" description="The first ever Ethernut board" mmc="false"></board> <board id="enut_2.1" name="Ethernut 2.1" description="The second version of the Ethernut" mmc="false"></board> <board id="enut_3.0" name="Ethernut 3.0" description="The third iteration, now with ARM!" mmc="true"></board> </catalog> </source>

Details

<source lang="c"> /* This filepointer holds the XML stream */ FILE *stream;

/* These are the tags we want to parse. Unspecified tags are NOT parsed to save resources. */ char *f_tags[] = { "board", NULL };

/* These are the attributes we want to parse. Unspecified attributes are NOT parsed to save resources. */ char *f_attribs[] = { "name", "description", "mmc", NULL };

/* Temporary node pointers. We'll use them to iterate through our XML tree later on. */ UXML_NODE *node; UXML_ATTRIB *attr; </source>

The stream is a filepointer to our XML file, example.xml, which is stored in uROM for this example. For more information on uROM access consult Reading UROM Files.

The f_tags and f_attribs variables define the tags and attributes we want to filter from the XML file. To conserve resources these are defined before actually parsing our XML file and we won't read any other tags and attributes. Note that the last element should always be NULL to signal the end of the list.

Lastly, we define temporary node and attr pointers which we will use to iterate through our XML tree later on.

<source lang="c"> unsigned long baud = 115200;

NutRegisterDevice(&DEV_DEBUG, 0, 0);

freopen(DEV_DEBUG_NAME, "w", stdout); _ioctl(_fileno(stdout), UART_SETSPEED, &baud);

puts("XML reading example\n");

if (NutRegisterDevice(&devUrom, 0, 0)) {

   printf("UROM error\n");

}

stream = fopen("UROM:example.xml", "r"); </source> We register our debug device, output the name of our sample application, mount the uROM and open our example.xml file.

<source lang="c"> products_tree = UxmlParseStream(stream, f_tags, f_attribs); </source>

This is the command that reads our stream and creates an XML tree from it. Note that we pass the previously defined f_tags and f_attributes list as parameters to tell which tags and attributes we want to parse. uxmlParseStream returns an XML tree which we will read and output in a moment.

<source lang="c"> node = products_tree; while (node) {

   puts("\n");
   attr = node->xmln_attribs;
   while (attr) {
       printf("\t%s: \t%s\n", attr->xmla_name, attr->xmla_value);
       attr = attr->xmla_next;
   }
   node = node->xmln_next;

} </source>

We assign our tree to a temporary node pointer. This is necessary since this pointer will change in the following and we don't want to mess up our original tree in the process.

While we have nodes, we now iterate through them, checking for attributes and setting our attr pointer to the next attribute afterwards. the node->xmln_next and attr->xmla_next attributes contain the next node/attribute in a linked list of nodes/attributes, so we can just cycle through them until we get a NULL as result.

Reading the name and value of an attribute is actually pretty easy. Just access the attr->xmla_name and attr->xmla_value attribute respectively.

<source lang="c"> UxmlTreeDestroy(products_tree); fclose(stream); </source> Don't forget to clean up afterwards. Especially XML trees can be real resource hogs so make sure you remove them after you don't need them anymore.

Output

<source lang="text"> XML reading example

       name:   Ethernut 1.3
       description:    The first ever Ethernut board
       mmc:    false
       name:   Ethernut 2.1
       description:    The second version of the Ethernut
       mmc:    false
       name:   Ethernut 3.0
       description:    The third iteration, now with ARM!
       mmc:    true

</source>

See also