Search code examples
xmlxml-parsingpugixml

Link to another XML node in the same document


XML file:

<?xml version="1.0"?>
<Common>
    <Test name="B1">
        <Test id="100"><Name>a test</Name></Test>
        <Test id="101"><Name>another test</Name></Test>
    </Test>
    <Test name="B2">
        <Test id="500"><Name>a simple test</Name></Test>
        <Test id="501"><Name>another simple test</Name></Test>
    </Test>
    <Test name="B6">
        <!-- link to B2 to avoid redundancy -->
    </Test>
</Common>

I wanna link the content of <Test name"B2"> to <Test name="B6"> to avoid retyping the same data! (the different names are necessary) Which statement is needed to refer to this XML node? (pugixml should be able to parse it correctly)


Solution

  • XML does not support references of this sort. You can solve this with custom "syntax" and custom C++ code, for example:

    <?xml version="1.0"?>
    <Common>
        <Test name="B1">
            <Test id="100"><Name>a test</Name></Test>
            <Test id="101"><Name>another test</Name></Test>
        </Test>
        <Test name="B2">
            <Test id="500"><Name>a simple test</Name></Test>
            <Test id="501"><Name>another simple test</Name></Test>
        </Test>
        <Test name="B6">
            <?link /Common/Test[@name='B2']?>
        </Test>
    </Common>
    

    Can be loaded with code like this:

    bool resolve_links_rec(pugi::xml_node node)
    {
        if (node.type() == pugi::node_pi && strcmp(node.name(), "link") == 0)
        {
            try
            {
                pugi::xml_node parent = node.parent();
                pugi::xpath_node_set ns = parent.select_nodes(node.value());
    
                for (size_t i = 0; i < ns.size(); ++i)
                    parent.insert_copy_before(ns[i].node(), node);
    
                return true;
            }
            catch (pugi::xpath_exception& e)
            {
                std::cerr << "Error processing link " << node.path() << ": " << e.what() << std::endl;
            }
        }
        else
        {
            for (pugi::xml_node child = node.first_child(); child; )
            {
                pugi::xml_node next = child.next_sibling();
    
                if (resolve_links_rec(child))
                    node.remove_child(child);
    
                child = next;
            }
        }
    
        return false;
    }