Skip to content
⌘ NSIS Forum Archive

XML plugin

118 posts

walb07#
Problem to parse XML with <!DOCTYPE ... []> element

Hi,

I've problems to parse an XML file which contains DTD description between xml header and first root element

Method ${xml::RootElement} returns with "-1". If I change the <!DOCTYPE ..[]> to a comment <!--DOCTYPE ...[]> which isn't really feasible it will work.

It would be helpful to support the DOCTYPE dtag by default.

example:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE RootElement [
<!ELEMENT RootElement (System+)>
<!ELEMENT System EMPTY>

<!ATTLIST RootElement Version CDATA #REQUIRED>
...
]>
<RootElement>
...
</RootElement>

with regards,
Dierk
jogijhelumi#
I'm trying to use XPathNode for expressions with namespace i.e. fns: Deployment
, but it fails and return error code -1.

Is there any trick/workaround to use it in this way.
FkYkko#edited
Hi I'm wondering if the attached plugin in this thread is the latest version of this plugin? It seems to me that the include file (xml.nsh) differs from the source code regarding some functions? For example, RemoveChild seems to return an error code while the include file and documentation does not specify any variable for this method. The RemoveAllChild function is not implemented in the source but is is availible in the documentation and include file?
Are there any updates regarding this? Maybe set up a simple sourceforge page to be able to update it?

or is there some other XML plugin which is generally used and better supported?

p.s. jogijhelumi: tinyxpath.sourceforge.net/ says that namespaces are not supported yet d.s.
Instructor#
Updated: TinyXPath to v1.3.1

Fixed: missing "xml::_RemoveAllChild" in DLL.
Fixed: ${xml::RemoveNode} incorrectly documented.


"XML" plugin v2.0

Download from Wiki
FkYkko#
Thanks for the quick reply.
As it happens, I still have some problems with this plugin and have changed to nsisXML (by W) which works after some rewrites.
Tzzz#
XML::Xxxx functions: doc to improve

Hello,

It seems to have ben many misunderstanding since the new XML::Xxxx functions were launched:

Added: ${xml::XPathString} -compute a string XPath expression.
Added: ${xml::XPathNode} -compute a node XPath expression and goes to it.
Added: ${xml::XPathAttribute} -compute an attribute XPath expression and goes to it.
I think that Instructor could get rid of most of them by improving the examples from its documentation:
In particular :
- Use of ${xml::RootElement} $0 $1 seems to fix many issues; please comment its use in XML::XPathNode, XML::XPathAttribute and XML::XPathString functions documentation whenever needed.
- Use of ${xml::XPathNode} "/configuration/components/component[@id='applicationSettings.default']/parameters/dbServerName" $0 is badly documented, i.e.:
. - XPath expression (e.g. "/a/b/@attr") is documented only for ${xml::XPathAttribute}, not for ${xml::XPathNode} whereas it seems to be afix for many cases.
. - Why is it that one shall use ${xml::XPathnode} prior to use a ${xml::SetAttribute} while logic would say to better use ${xml::XPathAttribute} instead. Instructor provides this kind of fixing but never fully explains why it is this way. Either fix ${xml::XPathAttribute} so that it allows to do what users think it should do, either fix the documentation so that its use is clearer compared to ${xml::XPathNode}.

Thank you very much for your plugin.
xbarns#
Hi all,

i need to edit a xml structure like this

<Configuration
<ConfigModule name="BlaBla">
 </ConfigModule>
 <ConfigModule name="FirebirdBridge" DefaultConfig="FALSE">
    <Section name="FirebirdBridge" description="Configuration for Firebird DB Bridge">
      <Data name="Host" type="string">127.0.0.1</Data>
      <Data name="Database" type="string">C:\Program Files\Test\Database\Test.FDB</Data>
      <Data name="User" type="string">User</Data>
      <Data name="Password" type="string">Password</Data>
    </Section>
  </ConfigModule>
</Configuration> 
In order to change the "<Data name="Database" type="string">C:\Program Files\Test\Database\Test.FDB</Data>" is there any other way then looping through the nodes and comparing attributes ?

i am already looping through the "ConfigModule" Nodes and comparing, is there an easier way?

Here is the code i have so far:

${xml::LoadFile} "$EXEDIR\scMaster.ctf" $0
loop:
${xml::FindNextElement} "ConfigModule" $0 $1
${xml::ElementPath} $0
${xml::GetAttribute} "name" $1 $2 
${IF} $1 == "FirebirdBridge"
        
    ${xml::GotoPath} "$0/Section/Data" $1
    Loop here again ?
         
         
${ELSE}
        Goto loop        
${ENDIF}
${xml::FindCloseElement}
${xml::SaveFile} "$EXEDIR\scMaster2.ctf" $0
${xml::Unload} 

Thanks for helping 🙂
freeDB_man#
I tried to use this plug-in with the special build of NSIS that has a string length limitation of 8192 instead of 1024 but the plug-in is indifferent to the change and only reads 1024 chars.

Any workaround?
spitze#
Hi all,
first of all great plugin. But i have a problem which i cannot solve.
I want to insert a line in an XML file.

The XML file looks like:


<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<adtf:configuration xmlns:adtv="adtv">
    <general_settings>
        <property name="gui_refresh_rate" value="25"/>
    </general_settings>
    <module_directories>
    </module_directories>
    <template_directories/>
</adtf:configuration>
Now i want to insert:

        <directory path="C:\program files\Test"/> 
between

<module_directories>
</module_directories> 

I tried this piece of code

${xml::FindNextElement} "module_directories" $0 $1
${xml::CreateNode} '<directory path="C:\program files\Test"/>' $4
${xml::InsertAfterNode} $4 $5 
The Output is:

<module_directories />
<directory path="C:\program files\Test"/> 
but it should be:

<module_directories>
  <directory path="C:\program files\Test"/>
</module_directories> 
Any suggestions??
Thanx
Instructor#
${xml::FindNextElement} "module_directories" $0 $1 
${xml::CreateNode} '<directory path="C😛rogram filesTest"/>' $4
${xml::InsertEndChild} $4 $5
radorl#
I'm editing XML file which uses &#DDDD; notation for non-ASCII characters. I noticed that after saving of modified XML file all &#DDDD; characters were replaced by original two-byte UTF-8 codes.

Is there some way how to instruct this wonderful plugin to use &#DDDD; notation?
casioclave#edited
need some help

Hi. Can U help me to change value in this stiring <value>en</value> from "en" to "ru"

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="inVisit.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<connectionStrings>
<clear />
<add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
<add name="inVisit.Properties.Settings.ivConnection" connectionString="Data Source=.\sqlexpress;AttachDbFilename=;Initial Catalog=ivData;Integrated Security=True;Connect Timeout=30"
providerName="" />
</connectionStrings>
<userSettings>
<inVisit.Properties.Settings>
<setting name="WinFormState" serializeAs="String">
<value>Normal</value>
</setting>
<setting name="WinFormSize" serializeAs="String">
<value>800, 600</value>
</setting>
...
<setting name="Language" serializeAs="String">
<value>en</value>
</setting>
<setting name="AutoCheckOut" serializeAs="String">
<value>False</value>
</setting>

</userSettings>
</configuration>
Guest#
I am trying to use this plugin to get values from an XML structure.

The document looks something like this:

<xml>
<groups>
<group id="1">
<name>Group 1</name>
</group>
<group id="2">
<name>Group 2</name>
</group>
<group id="3">
<name>Group 3</name>
</group>
</groups>
</xml>

I want to get the values of id and name and am currently working on getting value. After loading the document I can get the value of the first group (id 1) by doing this,
${xml::GotoPath} "/xml/groups/group" $0
${xml::GetAttribute} "id" $0 $1 
but I cannot get any others.

The code I am trying uses {xml::NextAttribute} and no matter what I do I alway get -1 returned for $2 and $0 and $1 are null. I originally tried it like this to get the loop, but it doesn't work even if I only run it once.
${xml::NextAttribute} $0 $1 $2
${While} $2 != -1
  ${xml::NextAttribute} $0 $1 $2
  MessageBox MB_OK $0
${EndWhile} 
Can anyone please tell me what I'm doing wrong and how I can access these values.
Guest#
Ok I have managed to get it working.

For anyone else with a similar issue here is what I did. (I used LastChild and PreviousSibling because I wanted to get the xml in reverse, replace with FirstChild and NextSibling to go through in order). Also, I make use of LogicLib which you will need to include if you're copying this example.

${xml::GotoPath} "/xml/groups" $0
${xml::LastChild} "group" $1 $0
${While} $0 == 0
  #get the group id
  ${xml::GetAttribute} "id" $1 $3
  #get the group name
  ${xml::FirstChildElement} "name" $3 $4
  ${xml::GetText} $2 $3
  #move to next group
  ${xml::Parent} $3 $4
  ${xml::PreviousSibling} "group" $1 $0
${EndWhile} 
sag47#edited
Here is a nice small example of using this plugin. In my installer header I included XML.nsh.

include "XML.nsh"
Here is a sample XML file that you should extract to the temp folder ($TEMP).

<?xml version='1.0' encoding='UTF-8' ?>

<account version='1.0'>
<account>
<protocol>prpl-jabber</protocol>
<name></name>
<settings>
<setting name='use-global-buddyicon' type='bool'>1</setting>
<setting name='require_tls' type='bool'>1</setting>
<setting name='ft_proxies' type='string'>gtalk.google.com:7777</setting>
<setting name='check-mail' type='bool'>0</setting>
<setting name='connect_server' type='string'>gtalk.google.com</setting>
<setting name='old_ssl' type='bool'>0</setting>
<setting name='auth_plain_in_clear' type='bool'>0</setting>
<setting name='buddy_icon_timestamp' type='int'>0</setting>
<setting name='port' type='int'>443</setting>
<setting name='custom_smileys' type='bool'>1</setting>
</settings>
<settings ui='gtk-gaim'>
<setting name='auto-login' type='bool'>1</setting>
</settings>
</account>
</account>
Basically what I wanted to do in the XML file above was to set text inside of the <name></name> XML node to text that is dependent on user options in my installer. Here's my NSIS code that I use to modify the above XML:
  ${xml::LoadFile} "$TEMP\\accounts.xml" $0
  IntCmp $0 0 +2 +1 +2
  MessageBox MB_OK|MB_ICONSTOP "Error loading accounts.xml!"
  ${xml::RootElement} $1 $0
  ${xml::FirstChildElement} "" $1 $0
  ${xml::FirstChildElement} "name" $1 $0
  DetailPrint "Setting [email]sam@gtalk.google.com[/email]/Home"
  ${xml::SetText} "sam@gtalk.google.com/Home" $0
  IntCmp $0 0 +2 +1 +2
  MessageBox MB_OK|MB_ICONSTOP "Error setting username!"
  ${xml::SaveFile} "" $0
  IntCmp $0 0 +2 +1 +2
  MessageBox MB_OK|MB_ICONSTOP "Error saving accounts.xml!"
  ${xml::Unload} 
Commands above explained...[list=1][*]${xml::LoadFile} "$TEMP\accounts.xml" $0 loads my XML file to be modified.[*]${xml::RootElement} $1 $0 selects the root node <account version='1.0'>[*]${xml::FirstChildElement} "" $1 $0 selects the first childnode located in the root node (which happens to be <account>). No argument means select the first childnode no matter what its name is.[*]${xml::FirstChildElement} "name" $1 $0 selects the first childnode within the node accounts > accounts that has the name "name" or <name></name>.[*]${xml::SetText} "sam@gtalk.google.com/Home" $0 sets the text inside of <name></name> so the result is <name>sam@gtalk.google.com/Home</name>[*]${xml::SaveFile} "" $0 saves changes that I've made to my XML file. No argument means save to the same file that was originally opened[*]${xml::Unload} unloads the XML plugin since I'm done modifying my XML file.[*]If you're wondering about IntCmp $0 0 +2 +1 +2, $0 is the error code returned by the given XML command. IntCmp compares the integer variable $0 with the integer 0 (zero). If $0 is equal to 0 then go down +2 commands (skipping the error message). If $0 is less than 0 then go down +1 command (show the error message). If $0 is greater than 0 then go down +2 commands (skip the error message). Skipping commands like that ignores comments.[/list=1]

Here is what my XML file looks like after being modified...
<?xml version='1.0' encoding='UTF-8' ?>

<account version='1.0'>
<account>
<protocol>prpl-jabber</protocol>
<name>sam@gtalk.google.com/Home</name>
<settings>
<setting name='use-global-buddyicon' type='bool'>1</setting>
<setting name='require_tls' type='bool'>1</setting>
<setting name='ft_proxies' type='string'>gtalk.google.com:7777</setting>
<setting name='check-mail' type='bool'>0</setting>
<setting name='connect_server' type='string'>gtalk.google.com</setting>
<setting name='old_ssl' type='bool'>0</setting>
<setting name='auth_plain_in_clear' type='bool'>0</setting>
<setting name='buddy_icon_timestamp' type='int'>0</setting>
<setting name='port' type='int'>443</setting>
<setting name='custom_smileys' type='bool'>1</setting>
</settings>
<settings ui='gtk-gaim'>
<setting name='auto-login' type='bool'>1</setting>
</settings>
</account>
</account>
Note: I changed XML values from my actual installer for the sake of this quick example to help get others started. So I would ignore the actual content because it has no use at all.

It took only a couple of minutes of reading the documentation that came with the XML plugin for NSIS that I created the above functions. So read the documentation for a better explanation of all of the capabilities possible with this wonderful plugin.

Sam Gleske
ChocJunkie#
What's the difference between using
${FirstChildElement} "name" $var1 $var2
and
${FirstChild} "name" $var1 $var2
for getting a element child? Especially, I'm interested what ${FirstChildElement} exists for, if the task can be done using ${FistChild}. A child can only be of type text or element. So that ${FirstChild} "Name" does search for a element child, doesn't it?

Thanks 🙂

CJ
ChocJunkie#
It think the plugin is missing a flug that declares the XML.nsh as already included!?

CJ
phungus420#
I'm not a programmer by trade and cannot figure out how to do what I need to do. As background I have an installer for a video game mod, however it only works if the user has patched the game, and it's a frequent issue where users don't patch and then complain that the mod causes a crash. I wanted to have the installer abort if the game isn't patched, and used a registry check, which works fine for 90% of users. Unfortunately a certain distributer (STEAM) does not sign a version key to their registry, so I can't check the version number using the registry for steam users. Also the script is a bit overly complex with all the various registry checks it needs to do anyway.

There is a simpler way, the game has an XML document that contains the version number. I tried to use the text reading function by Stu, but it fails, I assume because it's an XML file and not a text document. So looking around I found this plug in. Common sense would show that this plug in can be used to do what I want, unfortunately I can not figure out how to use it.

Basically I want to open up an XML file, I know how to get NSIS to find the file on the user's computer. Then I want to open it, go to line 10, and read what's there and return true if it's version is 319 and false if not. The specific XML tag is this:
	<Define>
<DefineName>CIV4_VERSION</DefineName>
<iDefineIntVal>319</iDefineIntVal>
</Define>
The iDefineIntVal is on line 10, and will always be on line 10. This is why this method would be superior to the registry check I'm currently using (which fails for some users).

Does anyone know how to read this, and get a true/false (or 1/0 etc) response if it's what it should be?
msroboto#
nsisXML::select /NOUNLOAD '/Define/iDefineIntVal'
${If} $1 != "0"
nsisXML::getText /NOUNLOAD
;the result should be in $3
${If} $3 == "319"
;do what you want
${Endif}
${Endif}
phungus420#
Originally posted by msroboto
nsisXML::select /NOUNLOAD '/Define/iDefineIntVal'
${If} $1 != "0"
nsisXML::getText /NOUNLOAD
;the result should be in $3
${If} $3 == "319"
;do what you want
${Endif}
${Endif}
Thanks for the response and help. But I can't figure this out. Where am I defining the file I am loading from? Also the whole document is iDefineIntVal tags like the above, I need it to read specifically the one on line 10, or the one that the XML is defining as CIV4_VERSION. As an example here is a snippet of the XML file:

	<Define>
<DefineName>CIV4_VERSION</DefineName>
<iDefineIntVal>319</iDefineIntVal>
</Define>
<Define>
<DefineName>MIN_VERSION</DefineName>
<iDefineIntVal>000</iDefineIntVal>
</Define>
<Define>
<DefineName>SAVE_VERSION</DefineName>
<iDefineIntVal>302</iDefineIntVal>
</Define>
...
Obviously I just want the CIV4_VERSION tag. So how do I:
1)define the XML file I'm reading this from (this file is on the User's computer, and I set the path off of the install directory; I can run a check for if the file exists easily, so defining it is easy, I just don't understand how to pass that to the XML NSIS function here)
2)Define which tag to read
ChocJunkie#
I have a problem in understanding the xml::unload function. What exactly does it do?
Does it unload the plugin or the loaded xml file?
If it unloads the plugin, how can I unload the loaded file?

Thanks. 🙂

CJ
pengyou#
Why do you want to "unload" the file?

If you have used the XML plugin to modify the file then use the plugin's "SaveFile" command.
ChocJunkie#
I just want to read the file, I don't need it to be in memory any more, if I've got what I was looking for. So I would like to erase/unload the file from the memory.
xbarns#
Hi all,

i am trying to use the plugin with the Unicode Version of NSIS but it won't even open the file correctly.

Am i just stupid or does it not work correctly with Unicode NSIS? (2.45)

I use the following script to test....


Name TestSetupSmall

OutFile TestSetupSmall.exe
;Request application privileges for Windows Vista
RequestExecutionLevel highest

!include "XML.nsh"

ShowInstDetails Show

Page Instfiles

Function .onInit
InitPluginsDir
FunctionEnd


Section TEST

SetOutPath $TEMP
File test.xml

${xml::LoadFile} "$TEMP\test.xml" $0

DetailPrint "-$0-"


SectionEnd
The provided example does not work either.

Any help would be much appreciated.

Thanks.
garygumdrops#
Problem if element has missing text when save

I think there is a bug with elements with blank text when the XML doc is saved:

<?xml version="1.0" encoding="utf-8" ?>
<Settings>
<Setting1>hello</Setting1>
<Setting2></Setting2>
</Settings>

if you save this using ${xml::save}
you'll see that the last element is not what it should be and appear as:
<Setting2 />
when it should be:
<Setting2></Setting2>

Everything is good if the element has any text inside it.
pengyou#
To an XML parser, the elements <sample/> and <sample></sample> are the same—they're both empty sample elements.

See Extensible Markup Language (XML) 1.0 (Fifth Edition):
gringoloco023#
FYI:

xml::_Coordinate isn't pushing an error/success value
So the macro, ${xml::Coordinate}, pops one to many !

BTW: great plug-in, anything else seems to work fine, and MSXML.dll independent :-)