Announcement

Collapse
No announcement yet.

Edit attributes in an XML with nsisXML by Wizou

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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:
    <global>
        <colors
          lava="orange"
          sky="blue"
        >
        </colors>
    </global>
    And I try to achieve this:
    HTML Code:
    <global>
        <colors
          lava="orange"
          sky="cyan"
          earth="brown"
        >
        </colors>
    </global>
    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?

  • #2
    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)

    Comment


    • #3
      The Wizou plugin uses MSXML and MSDN says: "Sets or updates the supplied attribute node on this element"
      IntOp $PostCount $PostCount + 1

      Comment


      • #4
        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.

        Comment


        • #5
          Originally Posted by pocz View Post
          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

          Comment


          • #6
            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>
            But instead I get this:
            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

            Comment


            • #7
              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. ..

              Comment


              • #8
                Originally Posted by pocz View Post
                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

                Comment


                • #9
                  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:
                  <?xml version="1.0" encoding="UTF-16"?>
                  <program:settings xmlnsrogram="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/"/>
                  <data>loads more data here</data>
                  </program:settings>


                  I want the installer to change one setting as follows:

                  code:
                  <my service="http://mynewsite.com/service2/"/>
                  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 $"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!

                  Comment


                  • #10
                    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?

                    Comment


                    • #11
                      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 '<?xml version="1.0" encoding="UTF-16"?><program><settings dontcare="" /><my service="http://mysite.com/service1/"/></program>'
                      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

                      Comment


                      • #12
                        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.

                        Comment


                        • #13
                          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

                          Comment


                          • #14
                            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. <my service=""/>

                            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:

                            <program><settings dontcare="" /><my service="http://mysite.com/service1/"/></program>

                            Which "settings" is it referring to from my sample above?

                            Is it this "settings"?
                            <program:settings xmlns:name="http://mysite.com" version="3">

                            Or this "settings"?
                            <settings setting1="whatever1" setting2="whatever2" setting3="whatever3"/>


                            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!

                            Comment


                            • #15
                              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 <settings dontcare="xyz"/> 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 <settings setting1="whatever1" setting2="whatever2" setting3="whatever3"/> but its name does not matter, I just slightly shortened your file.
                              IntOp $PostCount $PostCount + 1

                              Comment

                              Working...
                              X