Re: [xml] Obtaining a string result from an XPATH function?



On Sun, 17 Mar 2002, Horst wrote:

Hi, 
Perhaps this is not even possible, but I'm wondering how to get the
string result from an XPATH expression in my C code.
  xmlXPathInit();

  doc = xmlParseFile("foo.xml");
  if (doc == NULL) 
    return -1;

  ctxt = xmlXPathNewContext(doc);     
  obj  = xmlXPathEvalExpression("strcat(THIS,THAT)", ctxt); 

unfortunately this -
  printf ("String:%s user:%s user2 %s\n", 
   obj->stringval,
   obj->user,  
   obj->user2);
doesn't print anything

I'm not sure what you mean by "string result".  Are you trying to
extract the contents of the tag pointed to by the xpath expression in
foo.xml?  If so, I also am curious as to the best way to do it, but the
subroutine below is at least one way.  The xpath object contains a set
of pointers that indirectly lead to a list of nodes back in the original
doc (a list because a given xpath expression can return many nodes).  If
the xpath is unique there will be just one node in the list, and its
"children" are its contents.

I actually include two routines that I've found useful -- the second one
returns the value of an attribute associated with a given path if
possible.  I cannot guarantee that they are bug free yet, but at least
some pathways through are working pretty well in some of my code.  You
can ignore some of the comments as they are relevant to my application
that uses these routines.

If this isn't what you mean I will listen carefully as well, as I will
probably learn something.

BTW, to the list in general thanks very much for the help offered a week
or two ago -- I finally cleaned up all the leaky memory and segment
violations and yes, they were all my fault.

  rgb

-- 
Robert G. Brown                        http://www.phy.duke.edu/~rgb/
Duke University Dept. of Physics, Box 90305
Durham, N.C. 27708-0305
Phone: 1-919-660-2567  Fax: 919-660-2525     email:rgb phy duke edu


/*
 *========================================================================
 * xtract extracts the contents, by type of an xpath passed to it in
 * the context xp_doc (associated with some xmlDocPtr).  Each xpath passed 
 * MUST be unique in the tree to extract its value  -- if it is not 
 * xtract() returns the number of entries and a NULL 
 * value in the dest ptr.  Note that xtract does any requisite conversion
 * for you but you MUST pass the type explicitly.  Supported types are
 * currently CHAR, STRING, INT, FLOAT, DOUBLE, and MUST match the type
 * of the void ptr passed for the return value.
 *
 * Note that the reason I use type here and pass a pointer to the
 * variable I wish to set is so that the routine doesn't need to
 * allocate any new memory -- everything it uses it frees.  Eventually
 * the caller still has to free the xp_doc, though.
 *========================================================================
 */

/*
 *========================================================================
 * int xtract(int type, void *dest, char *xpath, xmlXPathContextPtr xp_doc)
 *========================================================================
 */
int xtract(int type, void *dest, char *xpath, xmlXPathContextPtr xp_doc)
{

 /* 
  * Number of matches found for the tag.  If numvals = 1, we
  * will extract and return the contents of the tag in *dest, suitably
  * converted to the appropriate type.  In any other case we return a
  * NULL or zero in the *dest pointer.  In all cases the value of the
  * return is numvals.
  *
  * This facilitates two kinds of usage.  xtract can be used to count
  * e.g. the number of cpus or interfaces (or any of the tags that
  * occur more than once with different id attributes in the xmlsysd
  * message).  It is also used to extract the CONTENTS of any UNIQUELY
  * specified tag.  The caller should always check to be sure that the
  * return is 1 in this case, indicating that it found exactly one tag
  * from which to obtain the value.
  *
  * Bad Things (tm) will happen if type doesn't exactly specify the
  * actual type of the void *dest pointer.  You have been warned.
  */
 int numvals = 0;
 
 /* xml (tree) node pointer, used to extract actual value */
 xmlNodePtr cur;
 /* xml (xpath) object pointer) */
 xmlXPathObjectPtr xp_op;

 /* 
  * Conversion pointers for the null dest pointer (inline casts don't 
  * seem to work in return()?!?) .
  */
 char *cptr;
 int *iptr;
 float *fptr;
 double *dptr;

 if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
   printf("DEBUG_EXTRACT_VALUES: xtract(%d,%x,%s,%x)\n",type,dest,xpath,xp_doc);
   printf("DEBUG_EXTRACT_VALUES. Use -v %d to focus.\n",DEBUG_EXTRACT_VALUES);
 }

 /*
  * Preload the return defaults -- all null.
  */
 switch(type) {
   case STRING:
     cptr = (char *) dest;
     /* 
      * K is a macro for 1024, which is a known/fixed buffer size, or 
      * roll your own...
      *
     strncpy(cptr,"",K);
     break;
   case CHAR:
     cptr = (char *) dest;
     *cptr = (char) 0;
     break;
   case INT:
     iptr = (int *) dest;
     *iptr = 0;
     break;
   case FLOAT:
     fptr = (float *) dest;
     *fptr = 0.0;
     break;
   case DOUBLE:
     dptr = (double *) dest;
     *dptr = 0.0;
     break;
 }

 /*
  * We start by using xpath to obtain a pointer to the node specified by
  * the path.
  */
 xp_op = xmlXPathEval(xpath, xp_doc);
 /* no such path in xp_doc */
 if( xp_op == NULL ) {
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_EXTRACT_VALUES: xpath %s not found!  Returning NULL.\n",xpath);
   }
   /* Free the path objects so we don't leak */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }
 if( xp_op->type != XPATH_NODESET ){
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_EXTRACT_VALUES: xpath type = %d is not a nodeset!  Returning NULL.\n",xp_op->type);
   }
   /* Free the path objects so we don't leak */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }

 /* 
  * This is the number of matches to the path above in the list.  If
  * we got here, it shouldn't really be zero.  We check anyway.
  */
 numvals = xp_op->nodesetval->nodeNr;
 /* if type is COUNT, we're done -- who cares about content? */
 if(type == COUNT) {
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_EXTRACT_VALUES: Found %d nodes for path %s.\n",numvals,xpath);
   }
   /* Free path object */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }

 /* Check to make sure we got at least one node */
 if(numvals <= 0){
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_XMLTRACT: xpath contains no nodes in the set!.\n");
   }
   /* Free path object */
   xmlXPathFreeObject(xp_op);
   return numvals = 0;
 }

 /* If we got more than one, return the count */
 if(numvals > 1){
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_XMLTRACT: xpath contains %d nodes, returning count!.\n",numvals);
   }
   /* Free path object */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }

 /*
  * If we get here, the only possibility is that xp_op contains a
  * table of pointers to xmlDoc nodes with a single entry -- the 
  * uniquely specified node we are looking for.  Its CONTENT lives 
  * here (under cur->content of here, actually):
  */
 if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
   printf("DEBUG_EXTRACT_VALUES: Found %d node for path %s.  About to extract value.\n",numvals,xpath);
 }
 cur = xp_op->nodesetval->nodeTab[0]->xmlChildrenNode;

 /*
  * I don't really know if this is an error or not.  I don't think that
  * it can happen if xp_op was successfully created and contains exactly
  * one node, but it is pretty harmless to check in case the xpath
  * evaluator didn't.
  */
 if( cur == NULL ){
   fprintf(stderr,"DEBUG_EXTRACT_VALUES Error: xtract() cur is NULL.\n");
   fprintf(stderr,"Yow!  This cannot happen.  Something is seriously broken.\n");
   exit(1);
 }
  
 /* 
  * At this point we've found the xml node, it is unique, we have a pointer
  * to it.  Its (xmlChar *) contents live in cur->content.  Piece of cake.
  * We now put it where it belongs in the correct format.
  */
 if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)) {
   printf("DEBUG_EXTRACT_VALUES: extracting %s\n",cur->content);
 }
 switch(type) {
   case STRING:
     strncpy(cptr,cur->content,K);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning string = \"%s\" in dest.\n",cptr);
     }
     break;
   case CHAR:
     *cptr = cur->content[0];
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning char = '%c' in dest.\n",*cptr);
     }
     break;
   case INT:
     *iptr = atoi(cur->content);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning int = %d in dest.\n",*iptr);
     }
     break;
   case FLOAT:
     *fptr = (float) atof(cur->content);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning float = %f in dest.\n",*fptr);
     }
     break;
   case DOUBLE:
     *dptr = atof(cur->content);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning float = %f in dest.\n",*dptr);
     }
     break;
 }

 /* Finally free the object pointer as we're done */
 xmlXPathFreeObject(xp_op);
 return numvals;

}

/*
 *========================================================================
 * int xtract_attribute(int type, void *dest, char *xpath, 
 *                        char *attribute, xmlXPathContextPtr xp_doc)
 *========================================================================
 */
int xtract_attribute(int type, void *dest, char *xpath,
                       char *attribute,  xmlXPathContextPtr xp_doc)
{

 /* 
  * Number of matches found for the tag.  If numvals = 1, AND we find
  * the attribute string, we will will extract and return the contents 
  * of the attribute in *dest, suitably converted to the appropriate 
  * type.  In any other case we return a NULL or zero in the *dest 
  * pointer.  In all cases the value of the return is numvals.  Note
  * that for this routine to work at all, the xpath MUST be unique.
  * The caller should therefore check that the returned value is 1.
  *
  * It (like xtract() can still be used to count the number of entries
  * with a specified path, but I can't imagine why you'd want to as then
  * the attribute specifier would be moot.
  *
  * Bad Things (tm) will happen if type doesn't exactly specify the
  * actual type of the void *dest pointer.  You have been warned.
  */
 int numvals = 0;
 
 /* xml (tree) node pointer, used to extract actual value */
 xmlNodePtr cur;
 /* xml (xpath) object pointer) */
 xmlXPathObjectPtr xp_op;

 /* 
  * Conversion pointers for the null dest pointer (inline casts don't 
  * seem to work in return()) .
  */
 char *attribute_value;
 char *cptr;
 int *iptr;
 float *fptr;
 double *dptr;

 if((verbose == DEBUG_ALL) || (verbose == DEBUG_FILL_VALUES)){
   printf("DEBUG_EXTRACT_VALUES: xtract_attribute(%d,%x,%s,%s,%x)\n",
              type,dest,xpath,attribute,xp_doc);
   printf("DEBUG_EXTRACT_VALUES. Use -v %d to focus.\n",DEBUG_EXTRACT_VALUES);
 }

 /*
  * Preload the return defaults -- all null.
  */
 switch(type) {
   case STRING:
     cptr = (char *) dest;
     strncpy(cptr,"",K);
     break;
   case CHAR:
     cptr = (char *) dest;
     *cptr = (char) 0;
     break;
   case INT:
     iptr = (int *) dest;
     *iptr = 0;
     break;
   case FLOAT:
     fptr = (float *) dest;
     *fptr = 0.0;
     break;
   case DOUBLE:
     dptr = (double *) dest;
     *dptr = 0.0;
     break;
 }

 /*
  * We start by using xpath to obtain a pointer to the node specified by
  * the path.
  */
 xp_op = xmlXPathEval(xpath, xp_doc);
 /* no such path in xp_doc */
 if( xp_op == NULL ) {
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_EXTRACT_VALUES: xpath %s not found!  Returning NULL.\n",xpath);
   }
   /* Free the path objects so we don't leak */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }
 if( xp_op->type != XPATH_NODESET ){
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_EXTRACT_VALUES: xpath type = %d is not a nodeset!  Returning NULL.\n",xp_op->type);
   }
   /* Free the path objects so we don't leak */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }

 /* 
  * This is the number of matches to the path above in the list.  If
  * we got here, it shouldn't really be zero.  We check anyway.
  */
 numvals = xp_op->nodesetval->nodeNr;
 /* if type is COUNT, we're done -- who cares about content? */
 if(type == COUNT) {
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_XMLTRACT: xpath returning node count = %d.\n",numvals);
   }
   /* Free path object */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }

 /* Make sure we got at least one node */
 if(numvals <= 0){
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_XMLTRACT: xpath contains no nodes in the set!.\n");
   }
   /* Free path object */
   xmlXPathFreeObject(xp_op);
   return numvals = 0;
 }

 /* If we got more than one, return the count (this is an error) */
 if(numvals > 1){
   if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
     printf("DEBUG_XMLTRACT: xpath contains %d nodes, returning count!.\n",numvals);
   }
   /* Free path object */
   xmlXPathFreeObject(xp_op);
   return numvals;
 }

 /*
  * If we get here, the only possibility is that xp_op contains a
  * table of pointers to xmlDoc nodes with a single entry -- the 
  * uniquely specified node we are looking for.  It lives here:
  */
 cur = xp_op->nodesetval->nodeTab[0];

 /*
  * I don't really know if this is an error or not.  I don't think that
  * it can happen if xp_op was successfully created and contains exactly
  * one node, but it is pretty harmless to check in case the xpath
  * evaluator didn't.
  */
 if( cur == NULL ){
   fprintf(stderr,"DEBUG_EXTRACT_VALUES Error: xtract_attribute() cur is NULL.\n");
   fprintf(stderr,"Yow!  This cannot happen.  Something is seriously broken.\n");
   exit(1);
 }
  
 /* 
  * At this point we've found the xml node, it is unique, we have a pointer
  * to it.  All that remains is to extract the contents of the attribute
  * of the given name, if it exists.  Note that we have to free the memory
  * when done.
  */
 attribute_value = (char *)xmlGetProp(cur, (const xmlChar *) attribute);

 /*
  * Finally, we run through the usual ritual of converting this to the
  * desired type, freeing both xp_op and attribute_value, and returning.
  */
 if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
   printf("DEBUG_EXTRACT_VALUES: extracting %s\n",attribute_value);
 }
 switch(type) {
   /* I don't know but hope that sizeof works for null pointers */
   case STRING:
     strncpy(cptr,attribute_value,K);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning string = \"%s\" in dest.\n",cptr);
     }
     break;
   case CHAR:
     *cptr = attribute_value[0];
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning char = '%c' in dest.\n",*cptr);
     }
     break;
   case INT:
     *iptr = atoi(attribute_value);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning int = %d in dest.\n",*iptr);
     }
     break;
   case FLOAT:
     *fptr = atof(attribute_value);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning float = %f in dest.\n",*fptr);
     }
     break;
   case DOUBLE:
     *dptr = atof(attribute_value);
     if((verbose == DEBUG_ALL) || (verbose == DEBUG_EXTRACT_VALUES)){
       printf("DEBUG_EXTRACT_VALUES: Returning float = %f in dest.\n",*dptr);
     }
     break;
 }

 /* 
  * Finally free the object pointer and attribute_value as we're done 
  */
 free(attribute_value);
 xmlXPathFreeObject(xp_op);
 return numvals;

}





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]