- NSIS Discussion
- XML plugin
Archive: XML plugin
Instructor
28th September 2005 19:52 UTC
XML plugin
XML plugin parses an XML document, and builds from that a Document Object Model (DOM) that can be read, modified and saved. XML plugin is based on TinyXml (2.4.0 RC).
Features:
- Read and/or modified xml files
- Code is fast and lightweight
- Unicode UTF-8 support
- "MSXML.DLL" independent
- Support for condense and non-condense white spaces
- Row and Column tracking
"XML" plugin v1.0
kike_velez
29th September 2005 09:17 UTC
:up: :up: :up: for
"MSXML.DLL" independent
>
Best Regards and Good Job
Instructor
20th October 2005 17:49 UTC
Changes:
- TinyXml updated to v2.4.1
- Fixed: output variable in xml::NoChildren
- New: xml::GetText
Convenience function for easy access to the text inside current element.
- New: xml::SetCDATA
Turns on or off a CDATA representation of the current element text.
- New: xml::IsCDATA
Queries whether this represents text using a CDATA section.
"XML" plugin v1.1
Comm@nder21
20th October 2005 19:58 UTC
next plugin i repack for correct usage of nsis file structure :)
Instructor
25th October 2005 05:23 UTC
-TinyXml updated to v2.4.2
-Fixed: "CreateNode" appearance of the "\r\n" at the end (thanks Lee Thomason)
-Fixed: Now error appears if current node is TEXT and uses "InsertEndChild"
-Changed: "AttributeByName" -> "GetAttribute" now not only returns value of the attribute, but also goes to it
-New: "GetNodeValue" can be useful in some cases
"XML" plugin v1.2
false
7th November 2005 18:58 UTC
How would I go about selecting a particular node by it's name? Like how Olivier Marcoux's NSIS XML uses...
nsisXML::select <XPath expression>
Thx!
Instructor
7th November 2005 19:35 UTC
If you want to "select" the node you need go to it (FirstChildElement, NextSiblingElement ...). Easy for me, when you ask the concrete example, what you trying to do.
false
7th November 2005 21:59 UTC
Somewhere nested in my XML, I have a set of child nodes who have the same name and share the same parent. The child nodes differ by having different attributes values.
I would like to test if a particular node exists in an XML without having to drill down using FirstChildElement and NextSiblingElement. If the node with the target node name exists, I would make it the current node.
I thought GotoHandle would do this for me but I am wrong in thinking it takes in a node name for a parameter.
Instructor
7th November 2005 22:33 UTC
I'll think about recursive node search
Instructor
9th November 2005 16:45 UTC
New: [xml::FindNextElement /NOUNLOAD "[name]" .r0 .r1]
-Search for element (recursive), returns the element name and goes to it.
New: [xml::FindCloseElement /NOUNLOAD]
-Closes search opened by xml::FindNextElement.
New: [xml::ElementPath /NOUNLOAD .r0]
-Returns the current element path.
(e.g. "/a/b/c[3]/d")
New: [xml::GotoPath /NOUNLOAD "[path]" .r0]
-Goes to the specified path.
(e.g. from root "/a/b[2]/c/d", from current element "a/b[2]/c/d",
using last element 'b' "a/b[-1]/c/d")
"XML" plugin v1.3
false
9th November 2005 16:56 UTC
You da man! That was a fast. I'll give this a shot. Thx
rsegal
9th November 2005 19:33 UTC
Hey Instructor any plans for UTF-16 support? I could really use that.
Instructor
9th November 2005 20:15 UTC
DOM builds by TinyXML parser, so UTF-16 is depend on this project. BTW: Where is already have feature request about it.
rsegal
9th November 2005 21:01 UTC
Ah excellent. Good to know it's on the queue.
Instructor
10th November 2005 15:04 UTC
Added: now "GotoPath" can accept elements without name (e.g. using any last element "a/[-1]/c/d")
"XML" plugin v1.4
Instructor
10th December 2005 17:21 UTC
Fixed: Now "ElementPath" and "GotoPath" correctly working with the document beginning. If "ElementPath" returns "" - the current element is the beginning of the document. [xml::GotoPath /NOUNLOAD "" .r0] - go to the document beginning.
"XML" plugin v1.5
Kiggie
13th January 2006 11:04 UTC
Bug in GetAttribute
Hi,
there seems to be a bug in the GetAttribute Method, it returns
always -1 for the first attribute of a node.
A possible workaround is to check if the FirstAttribute differs from the desired attribute.
Kind Regards,
Rob
chagam
13th January 2006 11:46 UTC
Hi,
May be I'm missing something but I don't understand how to replace a text value.
There's a xml::GetText function. How do I do a xml::SetText ?
Thanks in advance
Chag
Kiggie
13th January 2006 11:54 UTC
Originally posted by chagam
Hi,
May be I'm missing something but I don't understand how to replace a text value.
There's a xml::GetText function. How do I do a xml::SetText ?
Thanks in advance
Chag
Did you try the xml::SetNodeValue method ?
chagam
13th January 2006 12:25 UTC
Hi,
Yes, I tried. May be a did something wrong.
My xml file looks like this :
<config>
<local>
<host>test</host>
</local>
</config>
I tried :
xml::GotoPath /NOUNLOAD "/config/local/host" .r0
xml::SetNodeValue /NOUNLOAD "1234" .r0
and my new xml file looks like this :
<config>
<local>1234
</local>
</config>
Chag
Instructor
13th January 2006 13:26 UTC
Name "XMLTest"
OutFile "XMLTest.exe"
Section
xml::LoadFile /NOUNLOAD "test.xml" .r0
MessageBox MB_OK "xml::LoadFile$\n$$0=$0"
xml::GotoPath /NOUNLOAD "/config/local/host" .r0
MessageBox MB_OK "xml::GotoPath$\n$$0=$0"
xml::FirstChild /NOUNLOAD "" .r0 .r1
MessageBox MB_OK "xml::FirstChild$\n$$0=$0$\n$$1=$1"
xml::SetNodeValue /NOUNLOAD "1234"
MessageBox MB_OK "xml::SetNodeValue"
xml::SaveFile "test_saved.xml" .r0
MessageBox MB_OK "xml::SaveFile$\n$$0=$0"
SectionEnd
Instructor
13th January 2006 13:31 UTC
there seems to be a bug in the GetAttribute Method, it returns
always -1 for the first attribute of a node.
Fixed, thanks.
Instructor
13th January 2006 14:40 UTC
Fixed: GetAttribute returns -1 for the first attribute.
New: "SetText" -convenience function for easy set the text of the current element.
"XML" plugin v1.6
xilay
3rd March 2006 13:32 UTC
Hello,
I need to create a new element without text.
I use this code :
xml::LoadFile /NOUNLOAD "test.xml" .r0
xml
::GotoPath /NOUNLOAD "/root" .r0
xml::CreateNode /NOUNLOAD "<histo></histo>" .r0
xml::InsertEndChild /NOUNLOAD "$0" .r0
xml::SaveFile /NOUNLOAD "test.xml" .r0
>
But the new element is with a blank at the end, like this :
>
<
histo />
</root>
If I create the element with text, it's ok.
Have you got an idea?
Thanks
(sorry if my english is not very good)
Comm@nder21
3rd March 2006 15:13 UTC
this is correct xml behaviour.
empty elements have to be <element />, not <element></element>.
last one is no valid xml!
xilay
3rd March 2006 16:10 UTC
Yes, I know, the problem is the blank after 'histo'.
I have this <histo /> (you see, the blank between 'histo' and '/') and not this <histo/>.
Sorry for my bad explanation :(
Comperio
3rd March 2006 20:16 UTC
W3.org (which pretty much has the final word on markup languages like XML) recommends that a space should be used when declaring empty tags.
So this would be proper XML:
<histo />
NOT this:
<histo/>
See the section tags for empty elements at http://www.w3.org/TR/REC-xml/#sec-starttags for more info.
[edit]
And FWIW, I've always seen empty tags written using a space between the element name and the '/' character...
xilay
6th March 2006 08:10 UTC
Ok, I use xmlspy and it deletes the blank, so, I thought it was not normal.
Thanks a lot (and thanks for this perfect plugin)
Instructor
6th March 2006 13:50 UTC
Updated: TinyXml to v2.4.3
Changed: Now plugin used header "XML.nsh" for custom user variables and
better compile errors check.
Update from previous versions:
- Insert line in script:
!include "XML.nsh"
- Replace:
xml::LoadFile -> ${xml::LoadFile} ...
- Replace:
.r0 -> $0, .r1 -> $1 ... .R0 -> $R0, .R1 -> $R1 ...
"XML" plugin v1.7
stwhit
3rd May 2006 14:52 UTC
I need to be able to parse two XML documents at the same time (because I'm merging them during an upgrade process).
From looking at the API and the source code, it looks like this XML plugin only supports working on a single XML document at a time, because there is no concept of a file handle being returned by ${xml::LoadFile} and being passed to the other functions. It looks like there is a single, global XML document being edited (TiXmlDocument doc).
So, two questions:
1. Is it possible to edit two XML documents at the same time using this plugin?
2. If not, would you consider enhancing the plugin to use file handles to support editing multiple XML documents at once?
Thanks!
Instructor
3rd May 2006 15:19 UTC
1. Not.
2. I'm not plaining this.
But you can use ${xml::CloneNode}
Section
${xml::LoadFile} "file.xml" $0
MessageBox MB_OK "xml::LoadFile$\n$$0=$0"
${xml::GotoPath} "/" $0
MessageBox MB_OK "xml::GotoPath$\n$$0=$0"
${xml::CloneNode} $R0
MessageBox MB_OK "xml::CloneNode$\n$$R0=$R0"
${xml::Unload}
${xml::LoadFile} "file2.xml" $0
MessageBox MB_OK "xml::LoadFile$\n$$0=$0"
${xml::GotoPath} "/" $0
MessageBox MB_OK "xml::GotoPath$\n$$0=$0"
${xml::InsertBeforeNode} "$R0" $0
MessageBox MB_OK "xml::InsertBeforeNode$\n$$0=$0"
${xml::FreeNode} "$R0" $0
MessageBox MB_OK "xml::FreeNode$\n$$0=$0"
${xml::SaveFile} "file2.xml" $0
MessageBox MB_OK "xml::SaveFile$\n$$0=$0"
${xml::Unload}
SectionEnd
Comperio
31st July 2006 03:48 UTC
I'm trying to create an XML file from scratch using this plugin. Is this possible? If so, can someone post an example of how I might accomplish this?
Instructor
31st July 2006 15:56 UTC
FileWrite
Comperio
1st August 2006 00:48 UTC
darn...
I was hoping that I could build an XML file in memory using the plugin and then output the whole thing to a file. (It would be a cool enhancement for sure.)
I guess for now, I'll just write my own FileWrite functions to get me by for now.
Thanks for the feedback!
emmett_m
16th August 2006 07:00 UTC
Hi, I am hoping you can help me, I tried some of the code examples found in \NSIS\Examples\XMLXMLTest.nsi. And tried to apply these examples with what I want with little success, here is what I am trying to achieve.
I have the following xml file (VS.Net config file):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<appSettings>
<add key="somekey" value="some value"/>
</appSettings>
<connectionStrings>
<add name="ConnString" connectionString="TRYING TO MODIFY THIS TEXT"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
When the installer runs, I am trying to change the text in the connectionString attribute of the <add> element. I have placed the file in the same directory as my install script, and the script includes the following code:
!include "XML.nsh"
var /global ReplacementString
var /global fhandle
${xml::LoadFile} "appname.exe.config" $fhandle
MessageBox MB_OK "$fhandle" ; always returns -1
${xml::GotoPath} "/configuration/connectionStrings/add" $fhandle
${xml::SetAttribute} "connectionString" "$ReplacementString" $fhandle
${xml::SaveFile} "$INSTDIR\appname.exe.config" $fhandle
${xml::Unload} ; unload plugin
When attempting to open the file it always returns -1, can anybody tell me what the obvious mistake that I am making, and yes the file is in UTF8 format, I have however tried saving the format of the file from Notepad, into different formats in desperation, with no help (as I expected).
Instructor
16th August 2006 08:17 UTC
I have no problem. Make sure that the file exist and try to specify full path to it.
emmett_m
16th August 2006 09:00 UTC
That is interesting, I tried as you suggested to supply the full path to the file, which is the same location as the setup program, and it works. However I thought when you specified a file like:
${xml::LoadFile} "appname.exe.config" $fhandle
That it would use the file from the same directory as the installer, it would seem not. Also I tried the following:
${xml::LoadFile} "$EXEDIR\appname.exe.config" $fhandle
Which seems to be what I should have done. Thanks for your time ;-)
Comm@nder21
17th August 2006 18:47 UTC
as the plugin is extracted to $PLUGINSDIR i guess it searches the file there, not in $EXEDIR.
emmett_m
18th August 2006 00:59 UTC
Just in case somebody else comes across this problem, what I really wanted to do was have the .config file wrapped up into the installer, and manipulate that version, to do this I did the following:
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite ifnewer
File "appname.exe.config" ; reads file into memory and wraps(compresses) into installer
Section
${xml::LoadFile} "appname.exe.config" $fhandle ; modify the config file compressed in installer
This seems to work great.
JDaniels13
24th August 2006 22:49 UTC
I recently needed to edit existing .NET (Framework 1.1) config files within an NSIS install. Using some of the info here and the XML plugin docs, I was able to put something together that actually appears to work for changing keys in .NET 1.1 config files. Below is what I came up with. (There are some extra message boxes for debugging that should be removed prior to using.) Is there an easier way??!! I was truly surprised at the lack of discussion of this topic. I didn't want to write a .NET program in C# or VB.NET because I wanted it all to be in the NSIS setup, for simplicity's sake.
!include "XML.nsh"
var /global IOResult
var /global NewNodeHandle
var /global CurrentValue
var /global ElementName
!macro EditDotNETConfigFile ConfigFile KeyPath KeyName KeyValue
${xml::LoadFile} ${ConfigFile} $IOResult
${xml::GotoPath} ${KeyPath} $IOResult
IntCmp $IOResult 0 pathLookupSucceeded noAppSettings
pathLookupSucceeded:
MessageBox MB_OK "pathLookupSucceeded for ${KeyPath}"
checkAttributeExistence:
${xml::GetAttribute} "key" $CurrentValue $IOResult
; Did we find an existing instance of our config setting?
IntCmp $IOResult 0 checkKeyAttributeValue toNextElement
checkKeyAttributeValue:
MessageBox MB_OK "checkKeyAttributeValue current value is $CurrentValue vs desired key ${KeyName}"
; OK, found one, is it our desired key? Case-sensitive compare.
StrCmpS $CurrentValue ${KeyName} foundKeyAttribute toNextElement
toNextElement:
MessageBox MB_OK "checkKeyAttributeValue $CurrentValue didn't match ${KeyName}"
; Didn't find the <add key= " attribute name we wanted, check next "<add " element
${xml::NextSiblingElement} "add" $ElementName $IOResult
MessageBox MB_OK "toNextElement name is $ElementName IOResult is $IOResult"
IntCmp $IOResult 0 checkAttributeExistence noExistingEntry
foundKeyAttribute:
${xml::GetAttribute} "value" $CurrentValue $IOResult
MessageBox MB_OK "foundKeyAttribute check value attribute $CurrentValue vs desired key ${KeyValue}"
; Did we find an existing instance of our config setting?
StrCmp $CurrentValue ${KeyValue} valueUnchanged valueChanged
valueChanged:
${xml::SetAttribute} "value" "${KeyValue}" $IOResult
IntCmp $IOResult 0 setAttributeWorked setAttributeFailed
setAttributeWorked:
goto saveConfigFile
noExistingEntry:
MessageBox MB_OK "GetAttribute could not find existing value for ${KeyName}"
${xml::CreateNode} '<add key="${KeyName}" value="${KeyValue}" />' $NewNodeHandle
${xml::InsertAfterNode} $NewNodeHandle $IOResult
goto saveConfigFile
setAttributeFailed:
MessageBox MB_OK "SetAtribute failed for ${KeyName}"
goto cleanUp
noAppSettings:
MessageBox MB_OK "XPath not resolved for ${KeyPath}"
goto cleanUp
saveConfigFile:
${xml::SaveFile} ${ConfigFile} $IOResult
valueUnchanged:
; Just fall thru and unload
cleanUp:
${xml::Unload} ; unload plugin
!macroend
; Called as follows:
!insertmacro EditDotNETConfigFile "c:\InetPub\wwwroot\MyWebSite\Web.config" "configuration/appSettings/add" "connectionString" "MyTestConnString1a"
P.S. I tried the nsisXML plugin but it wanted to do "extra" things to the tags in the .NET config files, so I came back to this XML plugin and it's what I got to work first.