Best way to search and replace in an XML file?

I am regularly given XML files that are improperly formed in a particular way:

  • They all have a specific single line of XML text where they should have a specific 20-line block of XML text;
  • They all have a variable number of entries of a font name, that should be a different font name.

What's the most efficient/fastest acting approach to making a macro that will search and replace several selected XML files for this?

Thanks for your help.

I don't know about “efficient/fastest acting”, but you can simply and an XML file to a variable, and then make your changes and then write the file back.

Unless the files are very large, or there are a large number of changes, or a very large number of files, I can't imagine performance would be an issue.

Alternatively, use a text processing scripting language like perl.

Thanks for the reply. I'll give that a try.

I'm getting some unexpected results, so I clearly don't understand this well enough.

I expected the macro to do these steps:

  • Read the XML file that was dropped into the folder into a variable fileToOpen;
  • Search the variable for an XML string and replace it, writing it back to the variable;
  • Search the variable for all the occurrances of "Courier Prime" and replace them with "Courier Heavy", and write it back to the variable;
  • Write the variable to the original text file;
  • Move the file to another folder, showing that it's been converted.

Instead, it seems to create a repeating loop of all the steps, resulting in thousands of error messages: "File action failed because destination already exists"

How do I structure this to work properly?

Thanks!

When you write the new contents back to the original file, the system treats that as erasing the original file and creating a new one with the same name. That triggers the macro again, and so on in an infinite loop.

To get around this, use the Split Path action to pull the file name out of %TriggerValue%, save it to a variable, and write your converted text directly to ~/Desktop/XML/CONVERTED/<variable with file name>. Then delete the original file.

Here's an example that doesn't include any of the conversion steps:

2 Likes

When you write the new contents back to the original file, the system treats that as erasing the original file and creating a new one with the same name. That triggers the macro again, and so on in an infinite loop.

I had no idea why that was happening.

I used your implementation, and it's mostly (see below) working fine. Many thanks.

Deleting a file should not trigger a Folder trigger with "adds a file".

Keyboard Maestro deleting the file or anything else deleting the file makes no difference. Keyboard Maestro deleting or renaming the file would trigger the "removes a file". But deleting the file would not trigger the macro.

I would guess you are misunderstanding what is happening and that something else is going on. Maybe the macro is being triggered twice because of the way the file is being saved.

I expect you are absolutely right. I will look into it.

UPDATED:

I was misunderstanding, as you suggested.

Please take a look at this macro:
(I removed the Search And Replace Actions to simplify things.)

This macro does what it's supposed to do, but it also throws errors about two other files like this:

Read File action failed to read text file with error Error Domain=NSCocoaErrorDomain Code=264 "The file “.DS_Store” couldn’t be opened because the text encoding of its contents can’t be determined."

Delete File action failed because source does not exists .dat.nosync0e07.glz6cT

I'd appreciate any suggestions about how to implement this macro without throwing these errors.

The .DS_Store file has been added to the folder (generally the Finder does this when you first view a file or set some setting on it like sort order). So your macro triggers and tries to process the .DS_Store file.

Keyboard Maestro should probably ignore, or have an option to ignore, dot files, but it does not, so it's a file just like any other file.

Similarly, whatever is writing the file to that folder is first (or also) writing that “.dat.nosync0e07.glz6cT” file. Either it is writing it as well, or it is using it as some sort of “safe save” method, and writing the file and then renaming it to the final file.

I suggest you add to the start of your macro:

  • If text condition %TriggerValue% contains with "/."
    • Cancel This Macro

That will ignore any dot files and see how that goes.

Thanks, Peter. I ended up putting this at the start of my macro:

For some reason, the "starts with /." caught the .DS_Store file but not the .dat.nosync file, so I added the second condition.

Thanks for the help, it's working fine now, and will save me a lot of time.