1

i need to parse this xml file and use the input from the fields via a windows batch.i need to put all the values in variables, i am banging my head for so many hours but cant come up with anything useful. can someone please help ? A simple useful hint would suffice.

i need an answer which follows normal programming format as in first i need to see tag and then the and then if there are multiple i have to parse them in a loop.

    <USERS>
            <USER> 
                <USERNAME>FT_NRIAPIUSER </USERNAME>
                <PASSWROD>XXXXXXXXXXXXX</PASSWROD>
                <GROUPNAME>-</GROUPNAME>
                <POLICYNAME>-</POLICYNAME>
                <REMARKS>-</REMARKS>
            </USER>
            <USER> 
                <USERNAME>FT_SelfAdmin01</USERNAME>
                <PASSWROD>XXXXXXXXXXXXX</PASSWROD>
                <GROUPNAME>FT_SelfAdmins</GROUPNAME>
                <POLICYNAME>-</POLICYNAME>
                <REMARKS>-</REMARKS>
            </USER>
       </USERS>
<GROUPS>
    <GROUP>
        <GROUPNAME>FT_SelfAdmins</GROUPNAME>
        <POLICIES> 
            <POLICY>
                <POLICYNAME>-</POLICYNAME>
            </POLICY>
        </POLICIES>
        <REMARKS>-</REMARKS>
    </GROUP>
    <GROUP>
        <GROUPNAME>FT_SelfUsers</GROUPNAME>
        <POLICIES> 
            <POLICY>
                <POLICYNAME>-</POLICYNAME>
            </POLICY>
        </POLICIES>
        <REMARKS>-</REMARKS>
    </GROUP>
</GROUPS>
Subham Tripathi
  • 2,683
  • 6
  • 41
  • 70
  • 4
    Windows batch files are probably the most inappropriate tool for parsing XML - why does it have to be that? – Tim Pietzcker Feb 16 '15 at 11:19
  • 3
    You can parse XML in batch... if you hate yourself. I'm generally one to strive for a pure batch whenever possible, and I get that you're trying to do this without installing anything to your computer, but you should definitely look into using either PowerShell or JScript, which will also both be on your computer if you're using an OS later than XP. – SomethingDark Feb 16 '15 at 11:22
  • i understand your viewpoint but the client requirement compels me to use the batch. so if you can help me please tell me how to proceed. @SomethingDark – Subham Tripathi Feb 16 '15 at 11:28
  • I'd look at this: http://www.dostips.com/forum/viewtopic.php?p=32941#p32941 – SomethingDark Feb 16 '15 at 11:32
  • no all the values ,and that too in normal programming format as in first i need to see tag and then the and then if there are multiple i have to parse them in a loop. @npocmaka – Subham Tripathi Feb 16 '15 at 11:33
  • @SomethingDark , is it batch ? it doesnt look batch to me. :) – Subham Tripathi Feb 16 '15 at 11:36
  • "the client requirement compels me to use the batch" - you have my sympathies. If your client says this, it sounds unlikely he has any idea what he's talking about and might not notice if you use a PowerShell script instead... – Tim Pietzcker Feb 16 '15 at 11:45
  • @SubhamTripathi - it's a batch/JScript hybrid, but you run it like a regular batch file. If that doesn't work for you, the post directly under that one uses pur batch, but it has no comments and I haven't looked at it hard enough, so I have no idea how it works. – SomethingDark Feb 16 '15 at 11:48
  • answer of "pieh-ejdsch" @SomethingDark ? – Subham Tripathi Feb 16 '15 at 11:52
  • XML is a parsed data structure. Batch simply doesn't support that kind of parsing. Any chance you could sneak a perl script into your batch file? (You'd have to install perl, but the batch file could call the interpreter). – Sobrique Feb 16 '15 at 12:37
  • sorry @Sobrique , but i cant do that. – Subham Tripathi Feb 16 '15 at 12:38
  • I know the problem, and feel your pain. It really is a trivial answer if you've got a more serious scripting language to work with. – Sobrique Feb 16 '15 at 12:44
  • This may help: http://stackoverflow.com/questions/15718281/parsing-a-xml-file-using-batch-to-get-values-from-some-specific-nodes/15718666#15718666 – Magoo Feb 16 '15 at 14:10
  • i saw that and upvoted it but it is kind of hardcoded dont u think ? @Magoo – Subham Tripathi Feb 16 '15 at 14:12
  • @SubhamTripathi: Some days, people want a general principle and complain about a general approach. Other days they want a fully-customised solution. I remember that one - and it should be reasonably easy to define a variable containing the required tags to select, given the OP's problem as an example. But others may want a customised solution for tagset1, tagset2, tagset3.... – Magoo Feb 16 '15 at 14:38
  • sir i respect you a lot and have benefited a lot through your answers at SO in past . so please take my comments in best respect possible. :) @Magoo – Subham Tripathi Feb 16 '15 at 15:01

2 Answers2

1

Suppose you have z1.xml

<?xml version="1.0" encoding="utf-8" ?>
<USER>
<USERNAME>FT_NRIAPIUSER</USERNAME>
<PASSWORD>XXXXXXXXXXXXX</PASSWORD>
</USER>

And you have z2.bat

@echo off
for /f "tokens=2 delims=><" %%a in ('type z1.xml ^| find 
"<USERNAME>"') do set ip=%%a
echo User Name is %ip%

for /f "tokens=2 delims=><" %%a in ('type z1.xml ^| find 
"<PASSWORD>"') do set ip=%%a
echo Password is %ip%
pause

Now you can do this thus way.

65656565656
  • 91
  • 1
  • 12
1
@ECHO Off
SETLOCAL
:: 
:: remove variables starting $
FOR  /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="

:: evaluate command line. Structure is 
:: %1 : filename to be analysed
:: %2 : tag to signal "new data item"
:: %..: other REQUIRED tags
:: then repeat
:: /opt optional tags
:: /block start-analysis tag
:: /seq tags in required output-sequence
::
SET "$filename=%~1"
IF NOT EXIST "%$filename%" ECHO "%~1" NOT found&GOTO :EOF 
SET "$mode=tag"
:tagloop
SHIFT
SET "$=%~1"
IF NOT DEFINED $ GOTO process
IF %$:~0,1%==/ (SET "$mode=%$:~1%") ELSE (CALL SET "$%$mode%=%%$%$mode%%% %~1")
GOTO tagloop
:process

:: start mode OFF in block mode, on otherwise
:: establish tag1 (which signals start-of-next-data-item)
IF DEFINED block (SET "$mode=") ELSE (SET "$mode=Y")
FOR %%z IN (%$tag%) DO IF NOT DEFINED $tag1 SET "$tag1=%%z"
:: default output sequence is requiredtags optionaltags
IF NOT DEFINED $seq SET "$seq=%$tag% %$opt%"
CALL :zapvars
FOR /f "usebackqtokens=*" %%L  IN ("%$filename%") DO (
 FOR /f "tokens=1-3delims=<>" %%a  IN ("%%L") DO IF "%%c"=="" (CALL :lonely "%%a") ELSE (CALL :triplet "%%a" "%%b" "%%c")
 )
)

GOTO :eof

:: One parameter on line - may be start/end of block

:lonely
FOR %%p IN (%$block%) DO IF /i "%%p"=="%~1" (SET "$mode=Y"&CALL :zapvars) ELSE (
 IF /i "/%%p"=="%~1" CALL :output&SET "$mode=")
GOTO :eof

:: presume 3 elements - tag data endtag

:triplet
FOR %%p IN (%$tag1%) DO IF /i "%~1"=="%%p" CALL :output
FOR %%p IN (%$tag% %$opt%) DO IF /i "%~1"=="%%p" SET "$$%%p=%~2"
GOTO :eof

:output
IF NOT DEFINED $mode GOTO zapvars

:: build line for outputting - IF all the required elements are present

FOR %%p IN (%$tag%) DO IF NOT DEFINED $$%%p GOTO zapvars
SET "$line="

FOR %%p IN (%$seq%) DO IF DEFINED $$%%p (CALL SET "$line=%%$line%%,%%$$%%p%%") ELSE (CALL SET "$line=%%$line%%,""")
ECHO %$line:~1%

GOTO zapvars

:zapvars
FOR %%z IN (%$tag% %$opt%) DO SET "$$%%z="
GOTO :eof

Well - likely there's some holes in this - so "for experimental purposes"

Run it as thisbatch filename requiredtags

If there are optional tags, then add /opt optionaltags

To set the output field sequence, add /seq tagsrequiredtobeoutput - by default, use requiredtags+optionaltags

To activate output only between starttag and /starttag then add /block tag

The processing is reasonably simple. The tag names are built into envvars starting $ - $tag, $seq etc. The lines are assumed to be either <tag>, </endtag> or <tag>data</endtag>.

Beyond that, the process is controlled by $mode (when set : output) which is switched by the processing of a block tag. Data is accumulated into envvars $$tagname and will only be output if the $mode swich is set and there is a full set of required tags. The first required tag is special, it controls when a new data item is started (and starting a new data item implies that the previous item being accumulated gets output)

hence, the command

thisbatch q28540123.txt username passwrod /opt policyname groupname /seq policyname groupname username passwrod  /block users

would extract data <users>..</users>, requiring username passwrod and optionally policyname groupname and at each databreak on username (the first-mentioned required element) would show data in the sequence policyname groupname username passwrod

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • i am dumbstruck .. it took me around 2 hours to understand the answer and still confused at certain places and its so generic !! ... great answer sir ! – Subham Tripathi Feb 17 '15 at 08:18
  • 1
    Here's a magic line to aid in tracing the procedure : `set $&pause&echo===` Since all variables that start `$` are owned by the batch file (no "system" variables start `$`) then each time you use that debug line, you get an update on the variables' status. Put it in a loop and watch the variables change - there are sufficiently few that you can get a number of "breakpoints" on the screen. Anticipate what the next change should be, push a key to get the `pause` to proceed and get the results immediately. The reason for `$$` prefixes is simply that it allows tags "opt" "mode" etc to be processed – Magoo Feb 17 '15 at 09:05
  • why have u took all the vars starting with "$" – Subham Tripathi Feb 17 '15 at 09:17
  • 1
    Because there a re no system variables that start `$` and hence any variales that start `$` must have been created by this process. – Magoo Feb 17 '15 at 14:44
  • what is the use of `$$%%p` , what does this notation means ? what does this means `%%$$%%p%%` why are you referencing other variable names via some other variables, if i understand properly? – Subham Tripathi Feb 18 '15 at 08:58
  • `%%p` will contain one of the tagnames-of-interest from `$tag` or `$seq`, so it might contain ``passwrod` for instance. `$$passwrod` might be the variable being set to a value (interpreting `passwrod` tags) or being interrogated in `if defined $$%p` - this would see whether the variable `$$passwrod` has been set (ie. contains a value) – Magoo Feb 18 '15 at 14:30
  • how does this line of code "remove variables starting $" ? `FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="` – Subham Tripathi Feb 19 '15 at 08:12
  • can i return the variable `line` from this script. I need to return the line after certain improvisations, as the command line of this file is so important for the script to execute. normally what i do is i send a variable from the caller script as argument and then change in the script called, but how to do that with the command line of this script? – Subham Tripathi Feb 19 '15 at 08:20
  • 1
    `set $` will list each environment variable that starts `$` in the format `varname=varvalue`. If there are none, `2>nul` will suppress the error message generated. The caret is included to tell `cmd` that the `>` is part of the command to be executed, not the `for`. Hence `$hello=whatever` will be parsed using `=` as a delimiter and `%%a` will be assigned the value `$hello`. `set "$hello="` will clear the variable `$hello` from the environment. The `for` processes each line returned by `set` so every variablename returned (all variablenames starting `$`) will be cleared. – Magoo Feb 19 '15 at 08:46
  • 1
    No variable `line` is used by this script. The variable is `$line`. Since there is (potentially) more than one line produced by the script, returning "a" variable doesn't make much sense. Best to redirect the output to a file by using `thisscript parametes >afilename` then read `afilename` using `for /f` or try `for /f %%a in ('call thisscript parameters') do ...` Since the script uses `setlocal` to deliberately **not** affect the environment, it's a little late to add this extra requirement... – Magoo Feb 19 '15 at 08:54
  • i desperately need to return the output line from this batch. and i do not have the slightest clue, how to do that. can u please help me out ? – Subham Tripathi Feb 21 '15 at 15:46
  • can i refactor this code, is there a way ? ... please :) – Subham Tripathi Feb 21 '15 at 16:14
  • This code is a general approach. It has a huge amount of flexibility. The output may be many lines, so "the output line" makes little sense. It will output one or more lines to `stdout`, so you could redirect that output to a file using `thisbatch...parameters...>afilename` and then read the output file, or you could use `for /f "delims=" %%A in ('call thisbatch...parameters...') do echo %%A` where `echo %%A` could be `set "var=%%A` if you want to put the result in a variable. If you expect exactly one result value, then that *could* be accomplished quite easily. – Magoo Feb 21 '15 at 16:20
  • This way is working and it sums up the solution to whole problem i was having. Earlier i was reading the way you suggested before (reading from a file) .. Thanks a lot , u saved me ! – Subham Tripathi Feb 21 '15 at 17:16
  • what exactly is the use of `$mode` variable ? – Subham Tripathi Feb 22 '15 at 00:33
  • also `:zapvar` is used to clear (reset) the variables, am i correct sir ? – Subham Tripathi Feb 22 '15 at 00:45
  • 1
    $mode is set to `tag` or `block` or `opt` or `seq` depending on which switch was last encountered and controls which of `$tag`, `$block`, `$opt` or `$seq` accumulates the succeeding parameters. `:zapvar` indeed clears the variables specified. – Magoo Feb 22 '15 at 04:08
  • what is the use of `CALL SET "$%$mode%=%%$%$mode%%% %~1"`. i can understand the part before = that it is converting to $tag but not the part after it. – Subham Tripathi Apr 01 '15 at 09:06
  • also sir, what changes can i make to accomodate nested blocks for multiple policies, what i mean to say is i can have multiple policies they can vary from 0 to 20, in that case how can i know the number of policies are say,20. – Subham Tripathi Apr 01 '15 at 10:33
  • because with the above script, when i have two or more policies it always returns the last one and ignores the rest. – Subham Tripathi Apr 01 '15 at 11:13
  • The `call` line exploits how `cmd` parses the argument. It executes the parsed command as though it was a subroutine, so what is executed is `SET "$[$mode]=%$[$mode]% parameter1"` where [$mode] means the value of the variable `$mode` If `$mode` is `tag` then this evaluates to `set "$tag=%$tag% parameter1` If it's `opt` - then simply substitute `opt` for `tag`... – Magoo Apr 01 '15 at 16:43
  • I'll work on it further after I've had some sleep. What is the structure for "two or more policies"? – Magoo Apr 01 '15 at 17:00