Winamp & Shoutcast Forums Edit attributes in an XML with nsisXML by Wizou

 15th November 2021, 00:45 #1 pocz Junior Member   Join Date: Nov 2021 Posts: 8 Edit attributes in an XML with nsisXML by Wizou I can't figure it out. I try to modify an XML, but I get lost when finding the issue why it's not working. I have a file "foo.xml" HTML Code:   And I try to achieve this: HTML Code:   Let's assume I am not sure if the attributes sky and earth exist in the file, before editing. This is how my NSIS code looks like: code:nsisXML::create nsisXML::load "foo.xml" nsisXML::select "/global/colors" nsisXML::setAttribute "sky" "cyan" nsisXML::setAttribute "earth" "brown" nsisXML::save I haven't found any information in the documentation how to overwrite, add, or remove attributes with their values. Do I have to remove entire nodes, and append them again in the end?
 15th November 2021, 01:25 #2 pocz Junior Member   Join Date: Nov 2021 Posts: 8 I think nsisXML by Joel will be the easier solution to my problem. code:${nsisXML->removeAttr}${nsisXML->SetElementAttr} The simpliest way would be just to add the attributes, and hoping the old ones will be overwritten. But knowing life, I probably have to check the attribute to see if it exists, then eventually remove it, and then add new attributes: (Check if "sky" and "earth" exist, if they exist - remove them, create new attributes "sky" and "earth" with new values)
 15th November 2021, 10:49 #3 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 The Wizou plugin uses MSXML and MSDN says: "Sets or updates the supplied attribute node on this element" IntOp$PostCount $PostCount + 1  15th November 2021, 13:42 #4 pocz Junior Member Join Date: Nov 2021 Posts: 8 I got it. This in nsisXML by Wizou should be written in an other way, so it would work. code:nsisXML::create nsisXML::load "foo.xml" nsisXML::select "/global/colors" nsisXML::setAttribute "sky" "cyan" nsisXML::setAttribute "earth" "brown" nsisXML::save But it didn't work for me. But this below did work, in nsisXML by Joel. Last time updated in 2009. code:${nsisXML->OpenXML} "foo.xml" ${nsisXML->SetElementAttr} "/global/colors" "sky" "cyan"${nsisXML->SetElementAttr} "/global/colors" "earth" "brown" ${nsisXML->Release} "foo.xml" I didn't have to remove anything. There are no clones of attributes, it just works, and looks simple. If you have any ideas how the Wizous code should look like, please post your example. 15th November 2021, 18:59 #5 Anders Moderator Join Date: Jun 2002 Location:${NSISDIR}
Posts: 5,504
Quote:
 Originally Posted by pocz If you have any ideas how the Wizous code should look like, please post your example.
PHP Code:
 FileOpen $0 "$temp\test.xml" w FileWrite $0 '<?xml version="1.0" encoding="ISO-8859-1" ?><global><colors lava="orange" sky="blue"></colors></global>' FileClose$0 nsisXML::create DetailPrint $0,$1 nsisXML::load  "$temp\test.xml" DetailPrint$0,$1 nsisXML::select "/global/colors" nsisXML::setAttribute "sky" "cyan" nsisXML::setAttribute "earth" "brown" strcmp$0 0 +3 nsisXML::save "$temp\test.xml" nsisXML::release$0 nsexec::ExecToLog 'cmd.exe /C type "$temp\test.xml"' Pop$0 

IntOp $PostCount$PostCount + 1

15th November 2021, 19:58   #6
pocz
Junior Member

Join Date: Nov 2021
Posts: 8
Ah, yes. I forgot to save the file properly.
nsisXML::save requires an argument.

The downside of the two nsisXML versions is,
that the structure is being rebuilt.
This is the result I was trying to achieve:
HTML Code:
<global>
<colors
lava="orange"
sky="cyan"
earth="brown"
>
</colors>
</global>
HTML Code:
<global>
<colors lava="orange" sky="cyan" earth="brown">
</colors>
</global>
The problem is, that the XML file has about a hundred of attributes in a single node, and this many attributes in a single line is a bit messy. Anyway, at least other programs can read all the values properly.
Attached Files
 Foo.zip (700 Bytes, 136 views)

15th November 2021, 21:58   #7
pocz
Junior Member

Join Date: Nov 2021
Posts: 8
Quote:
 The problem is, that the XML file has about a hundred of attributes in a single node, and this many attributes in a single line is a bit messy.
Instead in an XML file, the dozens of attributes should have been stored in an INI file. It would be easier to read. ..

15th November 2021, 22:02   #8
Anders
Moderator

Join Date: Jun 2002
Location: ${NSISDIR} Posts: 5,504 Quote:  Originally Posted by pocz The downside of the two nsisXML versions is, that the structure is being rebuilt. This will only save some of the whitespace but it is the best you are going to get with MSXML I'm guessing PHP Code:  FileOpen$0 "$temp\test.xml" w FileWrite$0 '<?xml version="1.0" encoding="ISO-8859-1" ?>$\r$\n$\r$\n<global><colors lava="orange" sky="blue">$\r$\n$\r$\n</colors>$\r$\n$\r$\n</global>' FileClose $0 nsisXML::create DetailPrint$0,$1 strcmp$0 0 +2 System::Call $0->72(i-1) ; put_preserveWhiteSpace nsisXML::load "$temp\test.xml" nsisXML::select "/global/colors" nsisXML::setAttribute "sky" "cyan" nsisXML::setAttribute "earth" "brown" strcmp $0 0 +3 nsisXML::save "$temp\test.xml" nsisXML::release $0 nsexec::ExecToLog 'cmd.exe /C type "$temp\test.xml"' Pop $0  IntOp$PostCount $PostCount + 1  8th July 2022, 17:06 #9 MookyMook Junior Member Join Date: Jul 2022 Posts: 11 Hi I've got a similar issue, so hope this is the right place to post it? I'm trying to edit one setting in an existing settings.xml file, on upgrades. This is what the settings file looks like (for sake of anonymity, values obviously aren't literal): code: loads more data here  I want the installer to change one setting as follows: code: It should be simple? I've put Wizou's unicode nsisXML.dll in NSIS\Unicode\Plugins\x86-unicode Section in installer.nsi PHP Code: ${If} ${FileExists} "$SETTINGSDIR\settings.xml"        CreateDirectory "$SETTINGSDIR\backup" CopyFiles /SILENT "$SETTINGSDIR\settings.xml" "$SETTINGSDIR\backup\settings.xml" nsisXML::create nsisXML::load "$SETTINGSDIR\settings.xml"        StrCpy $0 "add" nsisXML::select '/program:settings/my' nsisXML::setAttribute "service" "http://mynewsite.com/service2/" nsisXML::save "$SETTINGSDIR\settings.xml"        nsisXML::release $0${EndIf}   The installer crashes after creating the backup, so the script is obviously wrong. Any help would be greatly appreciated. Thanks!
 9th July 2022, 10:07 #10 MookyMook Junior Member   Join Date: Jul 2022 Posts: 11 I've tried loads of different things from this thread and from the given example files, but the installer always crashes (with no errors). Does nsisXML not work with UTF-16? Or is it something to do with the structure or chars in the node names & attribs (colons, http://, slashes, quotes, spaces)? Or is just that the syntax has to be exact - but mine is always wrong? Anyone?
 9th July 2022, 15:26 #11 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 Check if$0 is 0 after nsisXML::load. If it is, it failed to load the XML file. PHP Code:  RequestExecutionLevel User Unicode True Section "Prepare example" Var /Global SETTINGSDIR StrCpy $SETTINGSDIR$ExeDir IfFileExists "$SETTINGSDIR\settings.xml" skip ; Create xml file if it is not already there FileOpen$0 "$SETTINGSDIR\settings.xml" w FileWriteUTF16LE /BOM$0 "" FileWriteUTF16LE $0 '' FileClose$0 skip: SectionEnd !include LogicLib.nsh Section nsisXML::create nsisXML::load "$SETTINGSDIR\settings.xml"${If} $0 P<> 0 nsisXML::select '/program/my' MessageBox mb_ok "$1 should not be 0 here"     ${If}$1 P<> 0         System::Call 'KERNEL32::GetTickCount()i.r5' ; Get arbitrary value in $5 nsisXML::setAttribute "service" "http://mynewsite.com/service2/?something=$5"         nsisXML::save "$SETTINGSDIR\settings.xml"${EndIf}     nsisXML::release $0${Else}     MessageBox mb_ok "load failed"     nsisXML::release $1${EndIf} SectionEnd  Why it does not like program:settings or its xmlns, I don't know. Probably related to MSXML somehow. IntOp $PostCount$PostCount + 1
 9th July 2022, 17:37 #12 MookyMook Junior Member   Join Date: Jul 2022 Posts: 11 Wow, thank you very much Anders. We're getting somewhere now. It compiles and doesn't crash. However, it's effectively creating a <1kb default settings file - which can also be achieved by just deleting settings.xml So it looks like just the "Prepare example" section is working, but nothing is happening for the nsisXML section. What I also didn't mention is there's a whole heap more user settings and data in the file which needs to be kept on upgrades. But that's why I was creating a backup, so at least nothing is lost. I'm also slightly embarrassed about getting the node/attrib/value wrong in my original script, but I know now. So again, thank you very much. I'll keep tinkering with it and will let you know how it goes.
 9th July 2022, 17:52 #13 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 Are you sure you copied the DLL from the binU directory in the .zip? My code works for me, it changes mysite.com/service1/ to mynewsite.com/service2/... IntOp$PostCount $PostCount + 1  9th July 2022, 19:52 #14 MookyMook Junior Member Join Date: Jul 2022 Posts: 11 Oh, I think I see what's happening now. You'll probably hate me for this, but I didn't realise we'd need to write a new temp transitional xml file first. I thought we could just directly edit the existing settings.xml file. The truth of the matter is that "http://mysite.com/service1/" is actually a random variable. It's a user override setting which could be any url. I want to change it to my new service url - or even just change it to empty, e.g. The user can then decide if they want to keep the new service or manually override it again with their own custom url. So I've no way of knowing what the exact custom user url will be. Can we specify a wildcard? Or is there a way to get that exact value first with nsisXML::getAttribute? Though I have actually tried setting it to the same custom url in both settings.xml and the FileWriteUTF16LE$0 line, but it still doesn't work. I just get a new settings.xml file - so I'm obviously still doing something wrong. And yes, I can confirm that binU \ nsisXML.dll is definitely in the "NSIS\Plugins\x86-unicode" folder. _____________________________________ Also, in this line: Which "settings" is it referring to from my sample above? Is it this "settings"? Or this "settings"? The reason being that those aren't the actual names, so I need to adapt it to my script accordingly. Sorry for the confusion. I really appreciate all the help!
 9th July 2022, 21:22 #15 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 You can edit a existing xml file directly. I changed my example a bit to reflect this. Until you can get my example to work correctly we can't really move on. By correctly I mean, you can edit the part in Notepad and those changes should remain if you run the code again (but you will get a new url part after "?something=" every time you run it). You can probably read the value but my example does not care what the value is, it just uses the attribute name ("service" in the "my" node). I was referring to but its name does not matter, I just slightly shortened your file. IntOp$PostCount $PostCount + 1  9th July 2022, 23:56 #16 MookyMook Junior Member Join Date: Jul 2022 Posts: 11 Thanks again Anders. Ok, so I don't need the installer to create an xml file. The program itself will create settings.xml with all the default values - on first run on clean installs. I just need to check and adjust that one existing setting on upgrades. The following are already declared/defined in the main nsi script so aren't required for this section: code:Unicode True RequestExecutionLevel Admin !include "LogicLib.nsh" Var SETTINGSDIR !define FILES_PATH Here's what I've got so far PHP Code:  Section "name3"SetOutPath$INSTDIR\BinFile ${FILES_PATH}\Bin\some.dllFile${FILES_PATH}\Bin\another.dllWriteRegStr HKEY_CLASSES_ROOT "foo" "" "blah"; If it exists, backup current component settings file first${If}${FileExists} "$SETTINGSDIR\path\to\name3\settings.xml"CreateDirectory "$SETTINGSDIR\path\to\name3\backup"CopyFiles /SILENT "$SETTINGSDIR\path\to\name3\settings.xml" "$SETTINGSDIR\path\to\name3\backup\settings.xml"; Force using new default service on upgradesnsisXML::creatensisXML::load "$SETTINGSDIR\path\to\name3\settings.xml"${If} $0 P<> 0 nsisXML::select '/program/my'${If} $1 P<> 0 nsisXML::setAttribute "service" "" nsisXML::save "$SETTINGSDIR\path\to\name3\settings.xml"            ${EndIf} nsisXML::release$0        ${Else} nsisXML::release$1    ${EndIf}${EndIf}SectionEnd   I've also tried the following variations: code: nsisXML::select '/program:settings/my' code: nsisXML::setAttribute "service" "http://mynewsite.com/service2/" I've also tried adding your extra lines: code: System::Call 'KERNEL32::GetTickCount()i.r5' ; Get arbitrary value in $5 nsisXML::setAttribute "url" "http://mynewsite.com/service2/?id=$5" Although I'm not sure what this does tbph. There can't be anything like /?id= after the service url. Was it just an example to show how an edit should work? The installer compiles and runs properly. The settings dir and file (in %AppData%) are not read-only. The folder has full write permissions etc. Everything works, except...when I go to the Settings folder, settings.xml has not been modified and the old custom value for still exists. I'm not sure what I'm doing wrong.
 10th July 2022, 00:38 #17 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 I asked you to get my example working before doing anything else! If you wish to go on alone; after nsisXML::load, MessageBox$0. After nsisXML::select, MessageBox $1 or$2 or whatever the plug-in readme says it writes to. IntOp $PostCount$PostCount + 1
 10th July 2022, 00:59 #18 MookyMook Junior Member   Join Date: Jul 2022 Posts: 11 Sorry. I couldn't get it working. As I said, all that happened was a new settings.xml file was created. I've tried everything you've suggested so far. I've tried over and over again. With your modified version, it (rightly) skips the "prepare example" section if settings.xml exists - and if it doesn't exist, I don't need the installer to create a new xml file. I've read the plugin readme again and there's no ref to MessageBox or similar. It's driving me crazy :-( If only it was a simple .ini file instead.
 10th July 2022, 01:18 #19 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 I know there is no reference to MessageBox, you just need to use it to see the return values. I changed my example, try again. IntOp$PostCount $PostCount + 1 10th July 2022, 08:49 #20 MookyMook Junior Member Join Date: Jul 2022 Posts: 11 Thanks for your patience Anders This is what I get with the revised example: Quote:  Program Installer 0 should not be 0 here  10th July 2022, 09:52 #21 Anders Moderator Join Date: Jun 2002 Location:${NSISDIR} Posts: 5,504 nsisXML::select is failing to find the node. 1) Delete the .xml and try again. Inspect the .xml, does the select path look valid? 2) Compile my example and upload the .exe here in a .zip IntOp $PostCount$PostCount + 1
 10th July 2022, 11:16 #22 MookyMook Junior Member   Join Date: Jul 2022 Posts: 11 Sure, I can do that (2), but before I do... It works if I manually change the line to just (and obviously also manually change the closing tag at the end to just ) The message is then "111135688 should not be 0 here" and the service url value is changed. However, the line exists on all installations, so we'd need to somehow change it and get the program itself to recognise the change ? We can't delete the existing xml file first because it can contain a ton of user data from over the years. For whatever reason (the colon, the xmlns, the extra params), possibly due to msxml, it's not finding the node.
 10th July 2022, 13:08 #23 MookyMook Junior Member   Join Date: Jul 2022 Posts: 11 It would work if there was some way for the installer to rename to just Then make the change to the "service" value. Then rename the node back to the original name. And all whilst keeping all the rest of the existing settings and data. But something tells me that's not possible?
 10th July 2022, 15:38 #24 Anders Moderator     Join Date: Jun 2002 Location: ${NSISDIR} Posts: 5,504 I wanted to get my example working first so we did not have to deal with too many issues at once. I assume there is a way to deal with namespaces but I'm no XML expert. Maybe you could try reading a bit about MSXML on MSDN... https://docs.microsoft.com/en-us/pre...763742(v=vs.85) https://docs.microsoft.com/en-us/pre...754539(v=vs.85) IntOp$PostCount $PostCount + 1  10th July 2022, 19:46 #25 MookyMook Junior Member Join Date: Jul 2022 Posts: 11 Thanks again Anders. Yes, we'll need to do some research and rethink things. I'll post here if I find a solution involving NSIS. 10th July 2022, 20:18 #26 Anders Moderator Join Date: Jun 2002 Location:${NSISDIR}
Posts: 5,504
According to https://docs.microsoft.com/en-us/dot...ace-navigation if you have
Quote:

the xpath expression is "/e:Envelope/e:Body/s:Search".

Notice xmlns:e, where your xml uses xmlns:name instead of xmlns:program or settings or whatever it is supposed to be.

IntOp $PostCount$PostCount + 1

10th July 2022, 21:16   #27
MookyMook
Junior Member

Join Date: Jul 2022
Posts: 11
So in my example:

code:<?xml version="1.0" encoding="UTF-16"?>
<program:settings xmlns:program="http://mysite.com" version="3">
<settings setting1="whatever1" setting2="whatever2" setting3="whatever3"/>
<moresettings setting4="whatever4" setting5="whatever5" setting6="whatever6"/>
<my service="http://mysite.com/service1/"/>
</program:settings>

The xpath expression for service="" attrib is...?

"/program:settings/my" ?

I tried that with your script and got "0 should not be 0 here" :-(

Quote:
 Originally Posted by Anders According to https://docs.microsoft.com/en-us/dot...ace-navigation if you have the xpath expression is "/e:Envelope/e:Body/s:Search". Notice xmlns:e, where your xml uses xmlns:name instead of xmlns:program or settings or whatever it is supposed to be.
Yes, that was originally a mistake on my behalf (in this thread).
The two values are actually the same in the xml:

<program:settings xmlns:program=...

 Winamp & Shoutcast Forums Edit attributes in an XML with nsisXML by Wizou