XPath Explained

Introduced in Alpha 17, new modding hooks were made available to manipulate XML without directly modifying the base XML files. This new mechanism makes use of XPath, which is an expression syntax for picking out nodes (or sets of nodes) in XML, as well as a few XML modification commands which act on the nodes chosen by the Xpath expression.

This page will go over some of the basics of XPath syntax as well as the 7D2D XML modification commands.

What is XPath?
XPath is an expression syntax that is used to select nodes and attributes in an XML Document. XPath does not do anything to the XML, it only selects nodes and attributes.

Very briefly, a node (or "element") in a well-formed XML document begins with a tag like "" and ends with a closing tag "" (the node includes the opening and closing tags as well as anything between them). A node may also be self-closing, with "/>" at the end of the opening tag and no closing tag. Every XML document has a "root" node which contains all other nodes. A node which is contained within another node is called a "child" node, the container is called its "parent" node, and all nodes at the same level are "siblings". Within a node's opening tag there can be attributes which show up as name='value' pairs.

Here is a simplified example from 7D2D's items.xml configuration file. is the root node and it has two child nodes (which are siblings to eachother). Both of the nodes have two child nodes. The and nodes have attributes like "name" and "value". (Note that in 7D2D, all useful information is stored as an attribute. So you will never see tool, instead it will be .)

      

An XPath expression is used to navigate through an XML document, selecting nodes and attributes that you want to work with. Within 7 Days to Die's modding framework, XPath is used to identify the nodes you want to change or remove, or the point where you want to add your own nodes. The game has implemented several XML modification commands, discussed in detail below, which take XPath expressions as parameters.

For more information on XML documents and syntax, visit this page at w3schools.com

For more information on XPath, visit this page at w3schools.com

This forum post from the A17 era provided much of the information at the heart of this page

XPath Syntax
The following examples show the basics of finding a node (or set of nodes) or an attribute (or set of attributes), including the use of conditionals which should cover most of what you'd need to do for basic 7D2D mods. See the w3schools.com link above for a full discussion of XPath expressions. Note that 7D2D may not support all standard XPath expressions.

'''All XPath expressions are case-sensitive. If your mod isn't working, double-check that you capitalize nodes and attributes exactly as in the 7D2D xml files.'''

The examples will use this very simplified (and slightly fake) example from 7D2D's items.xml:

    

<item name="Nailgun"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="Weight" value="7"/> <property class="Action0"> <property name="Reload_time" value="2"/> <item name="Shotgun"> <property name="Tags" value="weapon,ranged"/> <property name="Weight" value="12"/> <property class="Action0"> <property name="Reload_time" value="3.9"/>

Selecting a set of nodes
Selecting a set of nodes in XPath is as simple as giving the path from the root node down to your desired level:

<pre style="margin-left:20px">xpath="/items/item"

That XPath expression would select all nodes under the root node, meaning that the StoneAxe, Nailgun, and Shotgun nodes would all be selected. Note that without any conditions, you will select all nodes which match the level in your XPath expression. For example if we use this expression:

<pre style="margin-left:20px">xpath="/items/item/property"

We will select all nodes which are direct children of an node. So you'd get the "Tags", "Material", and "Weight" nodes from the StoneAxe, as well as "Tags", "Weight", and "Action0" from the Nailgun and Shotgun. It would not select the "Reload_time" nodes because they are not direct children of nodes. XPath selects all matching nodes at the level you specify anywhere in the XML document. See the 'Search All Levels' token below for a way to select all nodes regardless of level in the hierarchy.

Selecting a set of attributes
You can select a set of attributes by adding '@' to the attribute name at the end of your XPath expression.

<pre style="margin-left:20px">xpath="/items/item/@name"

That XPath expression would select all "Name" attributes from all nodes in the example, so the result set would include name="StoneAxe", name="Nailgun", and name="Shotgun". Just like when selecting nodes, without any condition to limit the results, you will get all matching attributes anywhere in the XML document at the level you specify.

The 'Search All Levels' Token
To search at all levels of the XML hierarchy you can insert '//' into your expression, which means "search anywhere below this spot regardless of level". As we showed above, if you wanted to get nodes which are direct children of nodes, you would use:

<pre style="margin-left:20px">xpath="/items/item/property"

But that doesn't match the "Reload_time" nodes because they are one level deeper. If you want all nodes under any, no matter how deep in the hierarchy they are, then you use '//':

<pre style="margin-left:20px">xpath="/items/item//property"

That says "start at every node under the root node, and select all nodes no matter how many levels deep they are." For the example XML above (as well as for the real 7D2D items.xml), this selects all nodes everywhere in the XML.

The 'Parent' Token
The parent token, '..', is useful when you need to find (and change) a node which has some attribute on one of its child nodes. You select the child node using a conditional (more details on these below) and then back up one node to the parent.

<pre style="margin-left:20px">xpath="/items/item/property[@class='Action0']/..

That expression will find nodes where the "class" attribute equals 'Action0' and then back up one level to select the parent node. For the example XML, the result is to select the Nailgun, and Shotgun nodes since those two have "Action0" child nodes.

XPath Conditionals
In 99% of cases you will want to work on a specific node or at least a carefully chosen subset. That is where conditionals come in. Since everything descriptive in 7D2D XML is put into an attribute, you will nearly always be using conditionals on attributes, i.e. looking for a particular item name, or zombie type, etc.

Equal To
By far the most-used conditional will be "equal to":

<pre style="margin-left:20px">xpath="/items/item[@name='StoneAxe']"

Note that the conditional value - 'StoneAxe' - must be put in single quotes. Note also the slight difference between selecting an attribute and filtering on (applying a conditional to) an attribute. When we selected attributes earlier, we put the attribute name at the end of the path with an @ sign:

<pre style="margin-left:20px">/items/item/@name   (SELECTING attributes)

However when we are applying a conditional, we need to put the attribute right next to the node which contains it and put it in square brackets.

<pre style="margin-left:20px">/items/item[@name='...']   (APPLYING CONDITIONAL to attribute)

You do not have to stop your XPath expression after you have applied a conditional - in fact most times you won't, because you probably want to make a change to some other attribute or sub-node. For example:

<pre style="margin-left:20px">(1) xpath="items/item[@name='StoneAxe']/property" (2) xpath="items/item[@name='StoneAxe']/property[@name='Tags']"


 * (1) will select all nodes for the item with @name="StoneAxe", so you'd get the "Tags", "Material", and "Weight" nodes in your result set.


 * (2) will specifically select the <property name="Tags"> node from the <item name="StoneAxe"> node.

Greater Than/Less Than
The greater than/less than conditionals work as you would expect, but due to XML syntax rules it isn't as simple as using. The problem is that '<' is not a valid character within an XML attribute and, when doing 7D2D XML modding commands (later on this page) your XPath expression is given in an attribute:

<pre style="margin-left:20px"><append xpath="your_xpath_expression_goes_here">...

So that means instead of '<' you have to use '&amp;lt;'. For greater than, you can use '>' if you wish, but you may want to use '&amp;gt;' instead, for consistency. Some examples:

<pre style="margin-left:20px">(1a) xpath="/items/item/property/property[@value &amp;gt; 2]" (1b) xpath="/items/item/property/property[@value > 2]" (alternate syntax using '>') (2) xpath="/items/item/property/property[@value &amp;lt; 4]"


 * (1a) and (1b) will select all nodes which are children of a node which is a child of an node, where the "value" attribute is greater than (&amp;gt;) 2. This would select the "Reload_time" property node of the Nailgun item's "Action0" property.


 * (2) will select all nodes which are children of a node which is a child of an node, where the "value" attribute is less than (&amp;lt;) 4. This would select the "Reload_time" propertie nodes for both the Nailgun and the Shotgun.

And, Or, and Not
You can combine conditions with "and" and "or" within the square brackets.

<pre style="margin-left:20px">(1) xpath="/items/item/property[@name='Weight' or @name='Tags']" (2) xpath="/items/item/property/property[@name='Reload_time' and @value &amp;lt; 4]"


 * (1) selects all nodes beneath an node where the @name attribute is either "Weight" or "Tags".


 * (2) selects all nodes beneath a node beneath an node where the property node's @name attribute is "Reload_time" and the @value is less than 4.

Note in (2) we still explicitly spelled out the full path to the we wanted: /items/item/property/property. But now we can use the "search anywhere" token (//) described above and make that expression more flexible:

<pre style="margin-left:20px">xpath="/items/item//property[@name='Reload_time' and @value &amp;lt; 4]"

That expression starts at every node and finds any node anywhere underneath it where the @name is "Reload_time" and the @value is less than 4. Even if the layout of 7D2D's XML changes somewhat, as long as there is a "Reload_time" property node somewhere between and, the XPath expression will still work.

You can also use "not" to select nodes where the condition is not matched:

<pre style="margin-left:20px">xpath="/items/item[not (@name='Nailgun')]"

That will select any nodes except the Nailgun item, i.e. the StoneAxe and Shotgun nodes. Finally, using parentheses within the conditional expression, you can combine and, or, and not for very complex selections.

<pre style="margin-left:20px">xpath="items/item//property[(@name='Reload_time' or @name='Weight') and not (@value &amp;lt; 5)]"

String Searches
There are three useful string-comparison conditionals: starts-with(s1,s2), ends-with(s1,s2), and contains(s1,s2). In all three, s1 is the string being searched and s2 is the value you are testing for. In almost all cases with 7D2D XML mods, s1 will be an attribute like @name or @value. Of course you can use "not" with any of these to select non-matching nodes.

<pre style="margin-left:20px">(1) xpath="items/item[starts-with(@name,'S')]" (2) xpath="items/item[ends-with(@name,'gun')]" (3) xpath="items/item//property[@name='Tags' and contains(@value,'tool')]/.." (4) xpath="items/item//property[@name='Tags' and not (contains(@value,'tool'))]/.."


 * (1) selects all nodes where @name starts with "S", matching the "StoneAxe" and "Shotgun" nodes.


 * (2) selects all nodes where @name ends with "gun", matching the "Nailgun" and "Shotgun" nodes.


 * (3) selects all nodes where @name="Tags" and @value contains the string "tool", then backs up one level in the hierarchy to grab the parent nodes. Read this as "select all nodes where the Tags has the word "tool" in it". The selected nodes would be the StoneAxe and Nailgun nodes.


 * (4) is the converse of (3) - it selects all nodes where the child "Tags" node does not have "tool" in its @value attribute. The selected node would be the Shotgun node.

XML Modification Commands
7D2D's XML mod engine supports several XML modification commands which use the XPath expression syntax above to select nodes and attributes for changes. The examples below will use the following simplified (and slightly fake) example from items.xml.

<pre style="margin-left:20px"> <item name="StoneAxe" category="axe"> <property name="Tags" value="axe,melee,light,tool"/> <property name="Material" value="Mstone"/> <property name="Weight" value="5"/> <item name="Nailgun" category="repair"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="Weight" value="7"/> <property class="Action0"> <property name="Reload_time" value="2"/>

Changing an Attribute (set)
The set command is used to change an existing attribute on a node. If the named attribute does not exist it does not create it and a warning will be sent to the log, but it will not prevent the mod from loading.

<pre style="margin-left:20px">(1) <set xpath="/items/item[@name='StoneAxe']/property[@name='Weight']/@value">3 (2) <set xpath="/items/item/property[@name='Weight']/@value">3


 * (1) changes the Weight value for the StoneAxe from 5 to 3.


 * (2) would change all Weight properties to 3, because there is no conditional on the nodes! Probably not something you'd ever want to do, but included here as an example of how XPath selects nodes.

Adding or Changing an Attribute (setattribute)
The setattribute command can be used to add a new attribute to a node or to change an existing attribute just like the 'set' command above.

<pre style="margin-left:20px">(1) <setattribute xpath="/items/item/property[@name='Weight']" name="measure">kilograms (2) <setattribute xpath="/items/item[@name='StoneAxe']/property[@name='Weight']" name="value">3


 * (1) adds a new attribute called "measure" with a value of "kilograms" to all nodes with @name='Weight'. The resulting XML for the StoneAxe would look like this (Nailgun would look similar):

<pre style="margin-left:20px"><item name="StoneAxe"> <property name="Tags" value="axe,melee,light,tool"/> <property name="Material" value="Mstone"/> <property name="Weight" value="5" measure="kilograms"/>


 * (2) changes the existing Weight value for the StoneAxe from 5 to 3, just like example (1) from the 'set' command.

Removing Nodes and Attributes (remove, removeattribute)
The remove command is used to remove nodes from the XML. If the node does not exist, a warning will be sent to the log, but it will not prevent the mod from loading.

<pre style="margin-left:20px">(1) <remove xpath="/items/item[@name='Nailgun']/property[@name='Tags']" /> (2) <remove xpath="/items/item[@name='Nailgun']" />


 * (1) removes the Tags from the Nailgun item.


 * (2) removes the Nailgun node completely.

The removeattribute command is used to remove an attribute from an XML node. If the attribute does not exist, a warning will be sent to the log, but it will not prevent the mod from loading. The following would remove the "category" attribute from the StoneAxe node:

<pre style="margin-left:20px"><removeattribute xpath="/items/item[@name='StoneAxe']/@category" />

The resulting node would look like this:

<pre style="margin-left:20px"><item name="StoneAxe">

Adding Nodes and Attribute Values (append)
The append command can be used to add entire nodes or to add values onto the end of an existing attribute. If we wanted to add an node for a shotgun into the example XML above, we could use this command:

<pre style="margin-left:20px"><append xpath="/items"> <item name="Shotgun" category="gun"> <property name="Tags" value="weapon,ranged"/> <property name="Weight" value="12"/> <property class="Action0"> <property name="Reload_time" value="3.9"/>

Everything between and will be added to the end of the nodes under the root node and it would look like this:

<pre style="margin-left:20px"> <item name="StoneAxe" category="axe"> <property name="Tags" value="axe,melee,light,tool"/> <property name="Material" value="Mstone"/> <property name="Weight" value="5"/> <item name="Nailgun" category="repair"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="Weight" value="7"/> <property class="Action0"> <property name="Reload_time" value="2"/> <item name="Shotgun" category="gun"> <property name="Tags" value="weapon,ranged"/> <property name="Weight" value="12"/> <property class="Action0"> <property name="Reload_time" value="3.9"/>

If we want to add an additional item into the @value attribute of to the Tags property of the Shotgun item, we could use this:

<pre style="margin-left:20px"><append xpath="/items/item[@name='Shotgun']/property[@name='Tags']/@value">,MyNewTag

Note that the leading comma is required in that case - the XML command appends exactly what you type. The resulting node would be:

<pre style="margin-left:20px"><property name="Tags" value="weapon,ranged,MyNewTag"/>

Adding Nodes Where You Want Them (insertAfter, insertBefore)
Unlike append, which adds nodes always to the end of the list (within the parent node), insertAfter will add nodes directly after the node(s) selected by your XPath expression. The new node(s) are added as 'siblings' at the same level as the node(s) from your XPath.

<pre style="margin-left:20px"><insertAfter xpath="/items/item[name='Nailgun']/property[name='Tags']"> <property name="AfterTags" value="tag1,tag2,tag3"/> </insertAfter>

The above command will add the AfterTags node into the Nailgun's node. Note that even though we specified a self-closing tag - <property ... /> - the XML mod engine actually expands it to use and opening/closing tags (along with a comment in between). This is functionally identical and does not change the effects of your XML modification.

<pre style="margin-left:20px"><item name="Nailgun" category="repair"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="AfterTags" value="tag1,tag2,tag3"> <property name="Weight" value="7"/> <property class="Action0"> <property name="Reload_time" value="2"/>

The insertBefore command works exactly the same as insertAfter - it just puts your new node(s) before the node(s) selected by your XPath expression.

<pre style="margin-left:20px"><insertBefore xpath="/items/item[name='Nailgun']/property[name='Tags']"> <property name="BeforeTags" value="tag1,tag2,tag3"/> </insertBefore>

The results of the above:

<pre style="margin-left:20px"><item name="Nailgun" category="repair"> <property name="BeforeTags" value="tag1,tag2,tag3"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="Weight" value="7"/> <property class="Action0"> <property name="Reload_time" value="2"/>

Advanced XPath Node Selectors
There are a few more advanced XPath operators which can be useful in some cases. The examples will use this very simplified (and slightly fake) example from 7D2D's items.xml:

<pre style="margin-left:20px"> <item name="StoneAxe"> <property name="Tags" value="axe,melee,light,tool"/> <property name="Weight" value="5"/> <property name="Material" value="Mstone"/> <item name="Nailgun"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="Weight" value="7"/> <item name="Shotgun" category="gun"> <property name="Tags" value="weapon,ranged"/> <property name="Weight" value="12"/>

Selecting First/Last Node Among Siblings
To select the first node among siblings, just put [1] after the node in your XPath expression. This example will put a new node after the "Tags" node for all nodes.

<pre style="margin-left:20px"><insertAfter xpath="/items/item/property[1]"><property name="MyProperty"></insertAfter>

To select the last node among siblings, use the last predicate. The example below will add a new above the "Material" property for the StoneAxe and above the "Weight" properties for the Nailgun and Shotgun.

<pre style="margin-left:20px"><insertBefore xpath="/items/item/property[last]"><property name="MyProperty"></insertBefore>

Selecting Nodes at Specific Relative Positions
To select nodes at specific points - e.g. the 4th node, or 3rd from the last - you can use a combination of the [n] and last syntax shown above as well as the position predicate.

<pre style="margin-left:20px">(1a) <insertBefore xpath="/items/item[@Name='StoneAxe']/property[2]"><property name="MyProperty"></insertBefore> (1b) <insertBefore xpath="/items/item[@Name='StoneAxe']/property[last-1]"><property name="MyProperty"></insertBefore> (2) <setattribute xpath="/items/item[@Name='StoneAxe']/property[position &amp;gt; 1]" name="foo">bar


 * (1a) and (1b) accomplish the same thing - inserting a new node before the "Weight" node of the StoneAxe item. (1a) does it by explicitly selecting the 2nd node as the target of insertBefore. (1b) does it by using "last-1" which means "one node before the last node".


 * (2) uses the position predicate and the greater than (&amp;gt;) operator to select all nodes where postion > 1, that is, everything after the first . It then adds an attribute "foo" with value "bar" to all matching nodes. The resulting XML would be:

<pre style="margin-left:20px"> <item name="StoneAxe"> <property name="Tags" value="axe,melee,light,tool"/> <property name="Weight" value="5" foo="bar"/> <property name="Material" value="Mstone" foo="bar"/> <item name="Nailgun"> <property name="Tags" value="tool,nailgun,sideAttachments"/> <property name="Weight" value="7" foo="bar"/> <item name="Shotgun" category="gun"> <property name="Tags" value="weapon,ranged"/> <property name="Weight" value="12" foo="bar"/>

Selecting Nodes Which Have a Specific Attribute
This is a special case of the [@attribute='value'] syntax covered at the start. By simply removing the ='value' part of the expression, you can select for all nodes which have the named attribute.

<pre style="margin-left:20px"><remove xpath="/items/item[@category]" />

That command would remove any node which has a "category" attribute which for the example XML would mean the Shotgun node gets removed.

Example - Infinite Horde
This is a working example which modifies gamestages.xml from A18/A19 so that the last wave of the Blood Moon Horde lasts all night. Here is a simplified snippet from gamestages.xml, which is the file that controls the horde spawns:

<pre style="margin-left:20px"> <spawner name="BloodMoonHorde"> <spawn group="feralHordeStageGS1" num="2" maxAlive="3" duration="1"/> <spawn group="feralHordeStageGS7" num="15" maxAlive="6" duration="1" interval="24"/> <spawn group="feralHordeStageGS10" num="15" maxAlive="6" duration="1"/> We want to make two changes to that XML. First, we need to set the "num" attribute on the last wave to a very high number - 99999 - so that the zombies never run out. Second, we need to eliminate the "duration" attribute of the last wave so that it doesn't time out before horde night ends at 4AM. By using the last predicate, we can make these changes with two commands:

<pre style="margin-left:20px"><set xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage/spawn[last]/@num">99999 <removeattribute xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage/spawn[last]/@duration" />

The resulting XML would be:

<pre style="margin-left:20px"> <spawner name="BloodMoonHorde"> <spawn group="feralHordeStageGS1" num="99999" maxAlive="3"/> <spawn group="feralHordeStageGS7" num="15" maxAlive="6" duration="1" interval="24"/> <spawn group="feralHordeStageGS10" num="99999" maxAlive="6"/>