xml_write: Writing XML data to disk

Previous: Functions except for xml_read ] [ Top: index ] [ Next: xml_string: Writing XML data to strings in memory ]

Writing the contents of one of our XML structures out into a file is simple. We've got two different variants on this function; one writes the entire element (xml_write) and the other writes just the content of the element (xml_writecontent).
 
XMLAPI void xml_write (FILE * file, XML * xml)
{
   ATTR * attr;
   ELEMENTLIST * list;
First, if the element we're working on is plain text, we just write it out.
 
   if (xml->name == NULL) {
      fprintf (file, "%s", xml->attrs->value);
      return;
   }
It's a regular element, so we open the element and write the name.
 
   fprintf (file, "<%s", xml->name);
   attr = xml->attrs;
   while (attr != NULL) {
      fprintf (file, " %s=\"%s\"", attr->name, attr->value);
      attr = attr->next;
   }
If the element has no children (this includes text), then we close the tag as an empty tag, and we're finished.
 
   if (xml->children == NULL) {
      fprintf (file, "/>");
      return;
   } else  fprintf (file, ">");
Otherwise we track down the list of children and write each of them, recursively.
 
   xml_writecontent (file, xml);
And finally, if there were children, then we need to close the tag with the full close.
 
   fprintf (file, "</%s>", xml->name);
}
The weakness of this function currently is that in the absence of plain text there will never be a line break. Not good -- but I don't see a good algorithm for doing it better while ruling out the possibility of inserting line breaks where they'll be errors. (May 27, 2001): Lately I've just been inserting linefeeds when I want pretty printing. The problem with that, of course, is the Unix/Windows CRLF issue. I'm still not sure how best to handle it.

Oh, well. Let's go ahead and define our xml_writecontent.
 
XMLAPI void xml_writecontent (FILE * file, XML * xml)
{
   ELEMENTLIST * list;

   list = xml->children;
   while (list) {
      xml_write (file, list->element);
      list = list->next;
   }
}
(May 27, 2001): we need similar functions to write valid (or pretty good, anyway) HTML given an XML-ish input. I should probably go look up the XHTML standard, but I'm too busy. If you use this library and want to tell me off, do so.
 
XMLAPI void xml_writehtml (FILE * file, XML * xml)
{
   ATTR * attr;
   ELEMENTLIST * list;

   if (xml->name == NULL) {
      fprintf (file, "%s", xml->attrs->value);
      return;
   }

   fprintf (file, "<%s", xml->name);
   attr = xml->attrs;
   while (attr != NULL) {
      fprintf (file, " %s=\"%s\"", attr->name, attr->value);
      attr = attr->next;
   }
If the element has no children (this includes text), then in XML we would close the tag as an empty tag, but in HTML we don't. That's difference number one. Also, a couple of tags should get full closes even if they're empty, so we'll handle that here.
 
   fprintf (file, ">");
   if (xml->children == NULL) {
      if (!strcmp (xml->name, "p") ||
          !strcmp (xml->name, "a")) {
         fprintf (file, "</%s>", xml->name);
      }
      return;
   }

   xml_writecontenthtml (file, xml);
And finally, if there were children, then only some HTML elements get a close tag, and others don't. That's the other difference, from the point of view of writing. (From the point of view of parsing, HTML is much, much uglier, which is the motivation for XML in the first place, really.)
 
   if (!strcmp (xml->name, "li") ||
       !strcmp (xml->name, "opt")) {
   } else fprintf (file, "</%s>", xml->name);
}
And of course we need the same thing for content-only:
 
XMLAPI void xml_writecontenthtml (FILE * file, XML * xml)
{
   ELEMENTLIST * list;

   list = xml->children;
   while (list) {
      xml_writehtml (file, list->element);
      list = list->next;
   }
}
(July 22, 2001) Due to Microsoft's lovely distinction between the file handles in the LIBC and MSVCRT runtimes (static and dynamic linking respectively), a simple xml_write can often fail if you've opened the file with a different runtime library than XMLAPI is using. Thus I came up with xml_output for handy debugging; I'm sure there are other uses as well, but that's what prompted me actually to write the function.
 
XMLAPI int xml_output (char * f, XML * xml, int mode)
{
   FILE * file;

   file = fopen (f, "w");
   if (!file) return 0;

   switch (mode) {
      case 1: xml_writecontent (file, xml); break;
      case 2: xml_writehtml (file, xml); break;
      case 3: xml_writecontenthtml (file, xml); break;
      default: xml_write (file, xml); break;
   }

   fclose (file);
   return 1;
}
Previous: Functions except for xml_read ] [ Top: index ] [ Next: xml_string: Writing XML data to strings in memory ]


This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. This presentation was created using LPML.