Sublime Forum

Advanced xml find & replace (within class)

#1

I hope this is the best place to post this. I’ve had a bit of a hunt around Google and maybe I’m not using the right search terms as xml is far from an area of my expertise! So any help on how to achieve this using Sublime would be greatly appreciated.

Not sure if I’m using the right terminology here but I’ve got a fairly large search and replace job on a xml I wouldn’t have a clue how to do! It’s a MIDI config for a DJ controller and it should have four deck control, each deck uses a separate MIDI channel (should make things a little simpler, so below is one single example of the Control glass of which there is one for every single control (or two for notes as there’s Note Off as well.) Within each class I need search the status string for a value and if I find a that value change the group string. I’m sure there must be some way to search treating each class as an object. It’s definitely much more along the lines of OOP than simple text editing though…

Example control class/object:

        <control>
            <group>[Channel1]</group>
            <key>quantize</key>
            <status>0x97</status>
            <midino>0x10</midino>
            <options>
                <normal/>
            </options>
        </control>

There are probably in the range of a couple of hundred such control Classes(??) within the main controls class which are not in any order so I can’t highlight the respective areas and search within that. Any idea what I would do??

0 Likes

#2

My suggestion would be to install https://packagecontrol.io/packages/xpath, it’s a great help when working with XML documents.

Then:

  1. Open Command Palette
  2. Type xpath q and select XPath: Query Document
  3. Type/paste //control/status[.="0x97"]/text()
  4. Scroll through the results/matches to ensure they look correct
  5. Select one of the results
  6. Open Command Palette
  7. Choose XPath: Re-run previous query and select all results
  8. Type what you want the new status to be

You could alternatively select all status text and use a regex replacement confined to the selection

2 Likes

#3

But that does does a bog standard search and replace and edits the same string as I just searched for! I need to find the classes from a search string based on a value on one line (status) and then within that class edit the value on a different line (group).

0 Likes

#4

In simple programming terms it would be something like:

for class = control
if status = 0x99
then group = [Channel3]

and loop through every control (and then output) class checking it. As it’s all contained in one class/object (as I would think of it in LUA and other OOPs) I would hope this would be possible. Maybe even easier with a Bash or Python script but I think I would be even more lost there…

0 Likes

#5

so just change the xpath expression to

//control[status="0x99"]/group/text()
2 Likes

#6

You absolute diamond! It brought to my attention that I don’t quite want to change every single one but after your second post the syntax has become obvious to me so easy to work out what I need from here. Thank you :slight_smile:

0 Likes

#7

Seems I’m going to have to do more manual editing and checking of this file than I would like and the order currently has absolutely no sense behind it! Can you let me know if the following would be possible with xpath or if I might have to look at xslt and/or xml dom? Having a quick skim of the w3schools pages on them both at the moment…

Example snip:

<parent>
<this>0xCA</this>
<that>0xB</that>
</parent>

So obviously I want to keep each parent element intact but order these hierarchically by contents of sub-elements. The contents of the sub-elements I want to order by are hex values.

Is it possible to run a command and reorder all the parent elements in order that the values of the sub-elements are in order A>B>C? As you can see this means treating the digits of one two-digit hex value separately…

EDIT:
Really don’t think xpath alone would do it. Closest command I got to selecting what I want it:
//control[status=“0x8*”]/descendant-or-self::*
but this has two issues. The wildcard isn’t recognised in the status search string. It only selects the element name and not the entirety of the element including contents like I need.

BUT even if this did get what I want the range of A and C would be small enough for me to run each step by hand. B on the other hand is 128 possible values so more than I would be willing to run, copy, paste, run again!

0 Likes

#8

Come on, there must be an easy way to select the entire element, rather than just the start tag itself, or the text() contents on the element. Googling on how to still only gives examples for selecting the start tag and incorrectly calls them the element so coming against a brick wall there. Have tried w3c and similar and nothing seems to give the right result…

EDIT: OK just found a stackoverflow thread that claims it is not at all possible! Not without XPath3 anyway, and from what I understand this plugin is using XPath1 correct. It seems to w3.org is down (at least I can’t access any page) so I couldn’t check the page to see how it should be done and check though… Can’t believe this wouldn’t be a standard function from the outset! Is it really one of the few things M$ did right? (It’s in .Net)

0 Likes

#9

Maybe set in your XPath package settings:

"goto_element": "entire",

and use query:

//control[starts-with(status, "0x8")]
1 Like

#10

Thanks a million, I can get myself a large chunk of the way there with that as A and C only have a small range off possible values. B is over 100 though! Hence I was in part trying this as proof of concept to then try and get it used in an external script (or can For Loops of similar be done in ST?) to loop across all possible values. Although even just sorting A and C will help me work within the file :slight_smile:

0 Likes