7 Days to Die Wiki
(→‎XPath Conditions: Added greater than and less than conditionals)
Boidster (talk | contribs)
m (→‎XPath Conditions: Fixed my own typos)
Line 202: Line 202:
   
 
Due to XML syntax rules, the character '<' cannot appear in an attribute like the xpath="..." attribute of the mod commands, so you must use '&amp;lt;' instead. You can use the '>' character for greater than if you wish, or you can use '&amp;gt;' for consistency. The below examples use entries from gamestages.xml, and would match Blood Moon Horde spawn stages less than/greater than 100 respectively (last two lines are functionally identical). (Tested as of A19.3)
 
Due to XML syntax rules, the character '<' cannot appear in an attribute like the xpath="..." attribute of the mod commands, so you must use '&amp;lt;' instead. You can use the '>' character for greater than if you wish, or you can use '&amp;gt;' for consistency. The below examples use entries from gamestages.xml, and would match Blood Moon Horde spawn stages less than/greater than 100 respectively (last two lines are functionally identical). (Tested as of A19.3)
<pre><append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage&amp;lt;100]">...some XML to append here...</apend>
+
<pre><append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage&amp;lt;100]">...some XML to append here...</append>
<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage&amp;gt;100]">...some XML to append here...</apend>
+
<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage&amp;gt;100]">...some XML to append here...</append>
<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage>100]">...some XML to append here...</apend></pre>
+
<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage>100]">...some XML to append here...</append></pre>
   
 
: starts-with( s1, s2 ) - Matches if s1 starts with whatever word you put in s2. In this example, @name is s1, and 'zombieTemp' is s2
 
: starts-with( s1, s2 ) - Matches if s1 starts with whatever word you put in s2. In this example, @name is s1, and 'zombieTemp' is s2

Revision as of 21:05, 13 March 2021

Introduced in Alpha 17, new modding hooks were made available to manipulate XML without directly modifying the base XML files.

This page will go over the xpath implementation, and provide examples on common syntax and tasks.

What is XPath?[ | ]


XPath is a method of path expressions that can select nodes, or node-sets in an XML Document. An xpath expression navigates through an XML document, finding nodes and attributes that you are looking for.

The implementation in 7 Days to Die allows a way of adding, changing, or removing XML lines and nodes, without directly editing the XML files, using xpath to find the nodes we want changed.

This means we can change individual attributes in the XML, add new ones and remove existing ones.

For more information on xpath, please visit the w3schools.com

This Post was generated from information from this forum post.

XPath Syntax[ | ]


XPath may look a bit intimidating when you are not familiar with it. This overview will break down what an xpath looks like when used against the XML files for 7 Days to Die.

An xpath format looks like this:

To find a node:

xpath="/rootNode/node"

To find a property:

xpath="/rootNode/node/property"

To find a attribute:

xpath="/rootNode/node/@attribute"


Those are very broad examples, but you can fine tune the syntax and only return the results you want.

<rootNode>
    <node name='MyOtherNode'>
    </node>
     <node name='MyNode'>
    </node>
</rootNode>

If you just wanted to access the MyNode, you can add conditions to your xpath:

xpath="/rootNode/node[@name='MyNode']/@attribute


XPath Examples[ | ]


Here are some xpath examples, and what they'll find, using the vanilla XML files from Alpha 17 experimental release.

File: Data/Config/loot.xml


<lootcontainers>
     <!-- Other loot container lines, removed for clarity -->
     <!-- minibike storage -->
     <lootcontainer id="62" count="0" size="4,3" sound_open="UseActions/open_shopping_basket" open_time="0" sound_close="UseActions/close_shopping_basket" loot_quality_template="baseTemplate">
     </lootcontainer>
     <!-- other loot container lines, removed for clarity -->
</lootcontainers>

If we wanted to grab a reference to size="4,3", our xpath would look like this:

xpath="/lootcontainers/lootcontainer[@id='62']/@size"

If we want to grab a reference to open_time:

xpath="/lootcontainers/lootcontainer[@id='62']/@open_time"


Here is a more complicated example of the items.xml.

File: Data/Config/items.xml


<items>
	<item name="meleeBoneShiv">
		<property name="Tags" value="knife,melee,light,weapon,meleeWeapon,perkDeepCuts,perkTheHuntsman"/>
		<property name="DisplayType" value="melee"/>
		<property name="SoundDestroy" value="wooddestroy1"/>
		<property class="Action0">
			<property name="Class" value="DynamicMelee"/>
			<property name="Damage_type" value="Slashing"/>
        <!-- Snip the rest of the file -->

If we want to grab a reference to what Tags a meleeBoneShiv has:

xpath="/items/item[@name='meleeBoneShiv']/property[@name='Tags']/@value

The above xpath line reads: "Under the Items node, look for the item with the name meleeBoneShiv, which has a property with the name of Tags, and look for the value attribute"


XPath Commands[ | ]

The above examples were to show how we can reference individual values. Alone, those references don't do anything. For those, we need to specify which xpath command we want to use. There are 5 primary xpath commands, with the first three being the most common ones to use.

set[ | ]

The set command is used to change individual attributes.

If we wanted to change the size of the lootcontainer from 4,3, we would use the following format:

<set xpath="/lootcontainers/lootcontainer[@id='62']/@size">5,3</set>
removeattribute[ | ]

The removeattributes is used to remove an existing attribute from an XML node

If we wanted to add a remove attribute called "author" on the meleeBoneShiv, we would do this:

 <removeattribute xpath="/items/item[@name='meleeBoneShiv']/@author" />

The result line would be:

<item name="meleeBoneShiv" >
setattribute[ | ]

The setattribute is used to add a new attribude to an XML node

If we wanted to add a new attribute called "author" on the meleeBoneShiv, we would do this:

 <setattribute xpath="/items/item[@name='meleeBoneShiv']" name="author">sphereii</setattribute>

The result line would be:

<item name="meleeBoneShiv" author="sphereii" >
append[ | ]

The append command is used to add either more nodes or more attribute values.

If we wanted to add another tag to the meleeBoneShiv, we would use the following format to add to the existing value:

<append xpath="/items/item[@name='meleeBoneShiv']/property[@name='Tags']/@value">,MyNewTag</append>

The resulting line would be:

<property name="Tags" value="knife,melee,light,weapon,meleeWeapon,perkDeepCuts,perkTheHuntsman,MyNewTag"/>


Append can also add blocks of code too. The following append command would add a new item at the bottom of the items root node:

<append xpath="/items">
       <item name="MyNewItem">
           <property name="Extends" value="meleeBoneShiv" />
       </item>
 </append>

Remove[ | ]

The remove command is used to remove nodes or attributes.

If we wanted to remove the Tags off of the meleeBoneShiv, we would use it in this way:

<remove xpath="/items/item[@name='meleeBoneShiv']/property[@name='Tags']" />

To remove the meleeBoneShiv completely:

<remove xpath="/items/item[@name='meleeBoneShiv']" />


insertAfter[ | ]

Much like append, insertAfter will add nodes and attributes after the selected xpath.

The snippet bellow allows you to add in the new panel in the windowVehicleStats window, after the rect called 'content', in the XUi/windows.xml:

<insertAfter xpath="/windows/window[@name='windowVehicleStats']/rect[@name='content']" >
	<panel pos="240, 0" style="header.panel">			
		<sprite style="header.icon" sprite="ui_game_symbol_add"/>
		<label style="header.name" text="COMBINE" text_key="xuiCombine"/>
	</panel>
</insertAfter>


insertBefore[ | ]

Much like insertAfter, insertBefore will add nodes and attributes before the selected xpath.

The snippet below does nearly the exact same thing as the above insertAfter, however, it will add the panel before the rect called 'content'.

<insertBefore xpath="/windows/window[@name='windowVehicleStats']/rect[@name='content']" >
	<panel pos="240, 0" style="header.panel">			
		<sprite style="header.icon" sprite="ui_game_symbol_add"/>
		<label style="header.name" text="COMBINE" text_key="xuiCombine"/>
	</panel>
</insertBefore>

In most places, using the append command would be suitable for our needs. However, certain places, we want to make sure that we've added the code in the right place. For example, if you are extending from a block, you'll want to be sure that your new code is added below the master block.

XPath Conditions[ | ]

Up until now, we've just been covering how to find xpath using direct matches (@name='toolbelt'), but xpath can provide us with more flexibility. Most of the time, you will not need these advanced conditionals, but be aware they exist for some more complex things you want to try.

> or &gt; (greater than) - Matches if the attribute value is greater than the given value.
&lt; (less than) - Matches if the attribute value is less than the given value.

Due to XML syntax rules, the character '<' cannot appear in an attribute like the xpath="..." attribute of the mod commands, so you must use '&lt;' instead. You can use the '>' character for greater than if you wish, or you can use '&gt;' for consistency. The below examples use entries from gamestages.xml, and would match Blood Moon Horde spawn stages less than/greater than 100 respectively (last two lines are functionally identical). (Tested as of A19.3)

<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage&lt;100]">...some XML to append here...</append>
<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage&gt;100]">...some XML to append here...</append>
<append xpath="/gamestages/spawner[@name='BloodMoonHorde']/gamestage[@stage>100]">...some XML to append here...</append>
starts-with( s1, s2 ) - Matches if s1 starts with whatever word you put in s2. In this example, @name is s1, and 'zombieTemp' is s2
<set xpath="/entity_classes/entity_class[starts-with(@name, 'zombieTemp')]/property[@name='Class']/@value">EntityZombieSDX, Mods</set>
ends-with( s1, s2 ) - Matches if s1 ends with whatever word you put in s2. In this example, @name is s1, and 'lateMale' is s2
<set xpath="/entity_classes/entity_class[ends-with(@name, 'lateMale')]/property[@name='Class']/@value">EntityZombieSDX, Mods</set>
contains( s1, s2 ) - Similar to starts-with, but s1 just needs to have some kind of match.
<set xpath="/entity_classes/entity_class[contains(@name, 'Template')]/property[@name='Class']/@value">EntityZombieSDX, Mods</set>
not - Flips the conditions around.
<set xpath="/entity_classes/entity_class[not (contains(@name, 'zombie'))]/property[@name='Class']/@value">EntityZombieSDX, Mods</set>

This would change every entity_class that does not contain the name zombie.

Another example:

<set xpath="/items/item[starts-with(@name, 'drinkJar') and not(contains(@name, 'Empty'))]/property[@name='Stacknumber']/@value">64</set>
and - Multi conditions. We used this above in the magazine_items, but we didn't explicitly type it out. An examples mean the same thing:
<set xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items' and @value='9mmBullet']/@value">9mmBullet,NoAmmo</set>
or - Multi conditions, but only needs to match one. In the below example, it'll only make changes if the property name is magazine_items OR if the value is a 9mm bullet.
<set xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items' or @value='9mmBullet']/@value">9mmBullet,NoAmmo</set>