XPATH surgery - Need help with xpath? post here.

Started by skullywag, June 02, 2017, 04:26:23 AM

Previous topic - Next topic

skullywag

Hey all,

XPATH is kind of my thing and ive been slowly pumping out info about best practice when using it and some others have posted some info on my behalf, like the performance hit of using // over / (if the tree is known there is no need to use //). I could put down a tonne of info on using xpath 1.0 but thought it better to get real world examples, so with that in mind, if you have a RW xpath issue, post it here and ill try and get back to everyone with any answers or relevant knowledge on any xpath subjects. We can then collate these examples into the relevant stickied help threads etc. (This is my way of helping out "modding" while I cannot actually mod right now)

Fire away. :)
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

Haplo

*Dialing Hotline*...ring, ring... ring, ring... "Hello, how may I help you?"

Hello XPath hotline.
I'd like to know from a professional side if my code is ok as it is.
It does work, but I'm just not sure if it's really the best way to do this?

So, here is my code:

<!-- Check if <comps /> exists. If not, add it -->
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[statBases/AccuracyLong > 0.05]/comps</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationAdd">
<xpath>*/ThingDef[statBases/AccuracyLong > 0.05]</xpath>
<value>
<comps />
</value>
</li>
</operations>
</Operation>

<!-- Find entries where AccuracyLong > 0.5 and add the new comp -->
<Operation Class="PatchOperationAdd">
<xpath>*/ThingDef[statBases/AccuracyLong > 0.05]/comps</xpath>
<value>
<li Class="WeaponRepair.CompProperties_WeaponRepairTwo2One">
<worktableDefs>
<li>TableMachining</li>
</worktableDefs>
<allowedDefSubName>Gun_</allowedDefSubName>
<compareQuality>true</compareQuality>
<jobDef>WeaponRepairTwo2One</jobDef>
<workTypeDef>Smithing</workTypeDef>
<maxRepair>0.95</maxRepair>
</li>
</value>
</Operation>


My questions: Is this useful or is there a better way to handle all available ranged weapons (only defs: Gun_*)?
Previously I tried ThingDef[defName="Gun_*"] but that didn't work and I didn't find an alternate call for that..

Thank you for your time ;D

AngleWyrm


I recently came across a situation where I wanted to specify more than one selection criteria, as the item I was looking for had a composite key. What I was attempting to do looked like this (but it generated errors):

someTraitDefType[ defName="traitgroup" and subType="2" ]/recordSubThingy

Is there another approach, or some expected grammatical pattern to this approach?
My 5-point rating system: Yay, Kay, Meh, Erm, Bleh

skullywag

Haplo, would this help?

/ThingDef/defName[starts-with(., 'Gun_')]

To extend this with some further info starts-with is in xpath 1.0 but there is no ends-with, to do that you can use a mix of substring and string length:

substring(., string-length(.)- string-length('wordtofind') +1)

Hope i understood the issue properly. Let me know if i didnt.
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

skullywag

AngleWyrm:

//ThingDef[defName='whatever' and /ThingDef/someotherthing/text()='sometext']/whatever

Should work fine, let me know if that doesnt make sense. I think the issue here might have been that you werent supplying the second AND a path to thing you wanted it to find, it wuld have been expecting subtype to be at the same level as defname, i could be wrong but thats how it seems to me. If you can give the exact xml you have and what youre trying to target i can help further.
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

milon

@Skully & Haplo, is there any reason that neither of you stickied this thread?

FYI, I un-stickied some old A12/A13 modding threads that haven't seen any action for a long time.  That should decrease our sticky-clutter.  :)

AngleWyrm

#6

Sometimes duplicate Add operation errors come up, and the situation in which they grow looks like this:

someDef[defName="aChosenThingy"]/entryToModify

The problem is that entryToModify may or may not exist in the record, and I want to add that entry if none currently exists.

Is there a best practice for this?
My 5-point rating system: Yay, Kay, Meh, Erm, Bleh

skullywag

AngleWyrm:

You need to test for its existence and add it if it doesnt exist, you can then add values to it in a further path if thats needed as well. as per:


<Operation Class="PatchOperationSequence">
  <success>Always</success>
  <operations>
    <li Class="PatchOperationTest">
      <xpath>//ThingDef[defName = "DiningChair"]/costList</xpath>
      <success>Invert</success>
    </li>
    <li Class="PatchOperationAdd">
      <xpath>//ThingDef[defName = "DiningChair"]</xpath>
      <value>
        <costList />
      </value>
    </li>
  </operations>
</Operation>

<Operation Class="PatchOperationAdd">
  <xpath>//ThingDef[defName = "DiningChair"]/costList</xpath>
  <value>
    <Cloth>5</Cloth>
  </value>
</Operation>
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

skullywag

Milon:

Stickied threads dont get seen much tbh, plus i was hoping the relevant people or myself would take from this thread and add to the tutorial threads that are about, 1 of which is stickied.
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

Sixdd

#9
Is there a way that I can make a sequence test not give an error when it fails to find the xpath? This is what I'm working with:
<Operation Class="PatchOperationSequence">
<success>Always</success>
<Operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[defName="VG_Biofuel"]</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationInsert">
<xpath>*/ThingDef[defName="FueledGenerator"]/comps/li[@Class="CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
<value>
<li>VG_Biofuel</li>
</value>
</li>
<li Class="PatchOperationInsert">
<xpath>*/ThingDef[DefName="FueledSmithy"]/comps/li[@Class="CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
<value>
<li>VG_Biofuel</li>
</value>
</li>
<li Class="PatchOperationInsert">
<xpath>*/ThingDef[defName="Forge"]/comps/li[@Class="CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
<value>
<li>VG_Biofuel</li>
</value>
</li>
</Operations>
</Operation>


This works with Vegetable Garden installed but I'm trying to make it work like an optional dependency, so if VG isn't installed it just ignores this operation.

EDIT: Nevermind, I commented the success element from the test operation and now it does the test without giving an error if it fails. Yay!

delheit

#10
I hope you can help I can't figure out what's wrong.

This works fine
<Operation Class="PatchOperationReplace">
    <xpath>/ThingDefs/ThingDef[defName = "MineableSteel"]/building/mineableYield</xpath>
    <value>
                <mineableYield>40</mineableYield>
    </value>
</Operation>


But this does not
<Operation Class="PatchOperationReplace">
    <xpath>/ThingDefs/ThingDef[defName = "Wall"]/costStuffCount</xpath>
    <value>
                <costStuffCount>10</costStuffCount>
    </value>
</Operation>


I get this error on the second one.

QuotePatch operation Verse.PatchOperationReplace(/ThingDefs/ThingDef[defName = "Wall"]/costStuffCount) failed

I have tried changing some things around like ThingDefs to Buildings or ThingDefs_Buildings but honestly both examples look so similar I am surprised it's not working the way it is.

Thank You

Update: I got it to work but I think I'm not suppose to do it this way.

<xpath>//ThingDef[defName = "Wall"]/costStuffCount</xpath>

Sixdd

#11
Yeah don't use the // instead try this

<xpath>*/ThingDef[defName = "Wall"]/costStuffCount</xpath>

The * at the beginning tells it to check all root tags where as the // tells it to check EVERY tag, even non root tags.

EDIT: To elaborate more, anywhere you place a * it will match "anything"

skullywag

Yep as sixdd has rightly stated, we as people that know the structure of the xml should never have a need for //

however in terms of your second patch costStuffCount doesnt exist on mineableSteel does it? thats what you are adding.
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

dburgdorf

I am virtually certain the answer to this question is "no," but I'll ask just for the sake of asking. Is there any way to use patching to create an entirely new def? Specifically, I'm wondering if there's any way to have a mod that adds plants also create seed defs if (and only if) "Seeds Please!" is in use, to avoid the need to have a secondary mod add them.
- Rainbeau Flambe (aka Darryl Burgdorf) -
Old. Short. Grumpy. Bearded. "Yeah, I'm a dorf."



Buy me a Dr Pepper?

Sixdd

#14
That is actually possible. Basically (in my understanding) as long as you have a root node to build off of you can add anything you like using an Add or Insert operation.

Also you can use a reference to a ThingDef in the mod you want to check for like this:

<Operation Class="PatchOperationSequence">
<success>Always</success>
<Operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[defName="EXP_Copper"]</xpath>
</li>
etc...


This returns true if the ThingDef with defName "EXP_Copper" exists. From there you just put more <li Class="someclass"> elements in for each operation you want to do.

Also I believe that Cupro has some kind of thing that checks for a mod name rather than checking for a defName.