RaptureXML icon indicating copy to clipboard operation
RaptureXML copied to clipboard

How about adding XML modification/updating support like follows

Open janky-h opened this issue 9 years ago • 0 comments

/**
 *  Update xml node values that matching specified XPath with feeded new value list.
 *  Note that the new value list can contain only one item which will be used to
 *  replace all matching nodes in xml, or contain just enough values to replace all matching nodes.
 *
 *  @param xpath
 *  @param nodeValue New value list, must have at least one item
 *
 *  @return
 */
- (BOOL) updateXpath:(NSString*)xpath withNewNodeValues:(NSArray *)nodeValue {

    xmlDocPtr doc = [self.xmlDoc doc];
    xmlXPathContextPtr xpathCtx;
    xmlXPathObjectPtr xpathObj;
    /* Create xpath evaluation context */
    xpathCtx = xmlXPathNewContext(doc);
    if (NULL == xpathCtx) {
        return NO;
    }
    /* Evaluate xpath expression */
    xpathObj = xmlXPathEvalExpression((xmlChar *)[xpath cStringUsingEncoding:NSUTF8StringEncoding], xpathCtx);
    if (NULL == xpathObj) {
        xmlXPathFreeContext(xpathCtx);
        return NO;
    }

    // Update
    xmlNodeSetPtr nodes = xpathObj->nodesetval;
    int size, i;
    size = (nodes) ? nodes->nodeNr : 0;
    if (size > 0 && size != [nodeValue count]) {
        return NO;
    }
    /*
     * NOTE: the nodes are processed in reverse order, i.e. reverse document
     *       order because xmlNodeSetContent can actually free up descendant
     *       of the node and such nodes may have been selected too ! Handling
     *       in reverse order ensure that descendant are accessed first, before
     *       they get removed. Mixing XPath and modifications on a tree must be
     *       done carefully !
     */
    for(i = size - 1; i >= 0; i--) {
        assert(nodes->nodeTab[i]);

        xmlNodeSetContent(nodes->nodeTab[i], (xmlChar *)[nodeValue[i] cStringUsingEncoding:NSUTF8StringEncoding]);
        /*
         * All the elements returned by an XPath query are pointers to
         * elements from the tree *except* namespace nodes where the XPath
         * semantic is different from the implementation in libxml2 tree.
         * As a result when a returned node set is freed when
         * xmlXPathFreeObject() is called, that routine must check the
         * element type. But node from the returned set may have been removed
         * by xmlNodeSetContent() resulting in access to freed data.
         * This can be exercised by running
         *       valgrind xpath2 test3.xml '//discarded' discarded
         * There is 2 ways around it:
         *   - make a copy of the pointers to the nodes from the result set
         *     then call xmlXPathFreeObject() and then modify the nodes
         * or
         *   - remove the reference to the modified nodes from the node set
         *     as they are processed, if they are not namespace nodes.
         */
        if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
            nodes->nodeTab[i] = NULL;
    }

    xmlXPathFreeContext(xpathCtx);

    return YES;
}

janky-h avatar Jan 25 '16 05:01 janky-h