1

I have an XML file, containing the following:

  <SubtitleTracks>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>false</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>1</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>false</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>2</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
  </SubtitleTracks>

The <SubtitleTrack> section can repeat numerous times throughout the file.

However the structure is always the exact same. The only thing that changes in this section is this:

<SourceId>0</SourceId>
<TrackNumber>1</TrackNumber>

then the next one will be:

<SourceId>0</SourceId>
<TrackNumber>2</TrackNumber>

and so on...

Right now, <Default>false</Default> is always the case.

What I would like to do is change <Default>false</Default> to <Default>true</Default> only when SourceId is 0 and TrackNumber is 1.

Can anyone please help translate these requirements into a batch file?

EDIT: I think I have almost got this, but it is choking on something, as the result I keep getting is <Default>false</Default> false

@ECHO OFF
SETLOCAL
SET "sourcedir=C:\Test"
SET "destdir=C:\Test"
SET "filename1=%sourcedir%\TestOutput.txt"
SET "outfile=%destdir%\TestOutputFixed.txt"
SET "hotsection="
CALL :clear$

(
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
 rem if line contains `<SubtitleTrack>` we've entered hot section
 ECHO "%%a"|FIND "<SubtitleTrack>">NUL
 IF NOT ERRORLEVEL 1 SET "hotsection=y"
 SET "saved="
 IF DEFINED hotsection (
  FOR /L %%r IN (100,1,999) DO IF NOT DEFINED saved IF NOT DEFINED $%%r SET "$%%r=%%a"&SET "saved=Y"
  rem if line contains `</SubtitleTrack>` or `</SubtitleTracks>` then end-of-hotsection
  SET "endhot="
  SET "endtrack="
  FOR /f "tokens=1delims= " %%w IN ("%%a") DO FOR %%x IN ("</SubtitleTrack>") DO IF "%%w"==%%x SET "endhot=Y"&IF 

"</SubtitleTracks>"==%%x SET "endtrack=Y"
  IF DEFINED endhot (
   rem end-of-hotsection
   rem check whether we have SourceID 0 and TrackNumber 1
   SET "id0="&SET "track1="
   FOR /f "tokens=1,*delims== " %%r IN ('SET $') DO (
    IF "%%s"==""<SourceId>0" SET "id0=Y" 
    IF "%%s"==""<TrackNumber>1" SET "track1=Y" 
   )
   rem found end-of-hotsection. now regurgitate saved lines and set `default` appropriately
   FOR /f "tokens=2delims==" %%r IN ('SET $') DO (
    echo "%%r"|FINDSTR /r /c:" *\<Default>" >NUL
    IF ERRORLEVEL 1 (ECHO %%r) ELSE (
     FOR /f "tokens=1delims=:" %%s IN ("%%r") DO (
      IF defined track1 (IF DEFINED id0 (ECHO %%s true,) ELSE (ECHO %%s false,)) ELSE (ECHO %%s false,)
     )
    )
    CALL :clear$
    IF DEFINED endtrack SET "hotsection="
   )
  )
 ) ELSE (ECHO %%a)
)
)>"%outfile%"

GOTO :EOF

:clear$
:: remove variables starting $
FOR  /F "delims==" %%z In ('set $ 2^>Nul') DO SET "%%z="
GOTO :EOF
StrangeThings
  • 59
  • 1
  • 7
  • 2
    Regex are the wrong tool to use here, have you considered using XSLT? – Allan May 22 '19 at 02:24
  • Beware all ye who enter here. https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 – lit May 22 '19 at 13:20
  • I have added some comments to my answer, can you check if it works for you? – Allan May 22 '19 at 23:14

2 Answers2

2

Input XML file:

$ cat subtitletracks.xml 
  <SubtitleTracks>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>false</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>1</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>false</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>2</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
  </SubtitleTracks>

Transformation sheet:

$ cat subtitletransform.xslt 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  >

    <!-- copy all nodes and attributes -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- when find a default node for which the SourceId = 0 and trackNumber = 1 -->
    <xsl:template match="//SubtitleTrack/Default[../SourceTrack/SourceId = '0' and ../SourceTrack/TrackNumber = '1']">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <!-- assign value to true -->
            <xsl:value-of select="'true'"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Output:

$ xsltproc subtitletransform.xslt subtitletracks.xml 
<?xml version="1.0"?>
<SubtitleTracks>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>true</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>1</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>false</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>2</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
  </SubtitleTracks>

Notes:

On windows to run XSLT processor via command line have a look at: Are there any XSLT processing command line tools?

Allan
  • 12,117
  • 3
  • 27
  • 51
  • I have tried this out using libxslt-1.1.26.win32. It runs successfully with no errors against the file, but the output does not change for me like above for you. Default remains as false. What am I missing? – StrangeThings May 22 '19 at 03:40
  • @StrangeThings: That's very strange... Can you try with another tool? Or even directly via `saxon`? – Allan May 22 '19 at 06:45
  • @StrangeThings: you need to redirect the output of the command to a new file `xsltproc.exe -v subtitletransform.xslt subtitletracks.xml > new_file.xml` – Allan May 22 '19 at 22:15
  • @StrangeThings: you can also use option `-o output.xml` to define the output file to write to http://xmlsoft.org/XSLT/xsltproc.html – Allan May 22 '19 at 23:12
  • output message "Warning: program compiled against libxml 207 using older 206" – StrangeThings May 22 '19 at 23:40
  • @StrangeThings: have you checked the output file content? – Allan May 22 '19 at 23:43
  • @StrangeThings: perfect!!! If I have solved your question could you accept my answer/upvote? Thanks – Allan May 23 '19 at 00:32
1

Your restriction to implement with CMD batch file is adding complexity and degrading performance.

Batch files are used to integrate and synch program execution.

Your task is more about text/xml processing program.

A more suitable tool will be perl/python/awk scripts. These languages are designed for text processing.

Here is a sample 6 lines awk program:

awk '
pass1 && /<Default>false<\/Default>/ {markedLine = NR} 
pass1 && /<SourceId>0<\/SourceId>/ {sourceIdFlag = 1; next}
pass1 && sourceIdFlag && /<TrackNumber>1<\/TrackNumber>/ {markedLinesArr[markedLine] = 1}
pass1 {sourceIdFlag = 0}
!pass1 && markedLinesArr[FNR] == 1 {print "      <Default>true</Default>";next}
!pass1 {print}
' pass1=1 input.xml pass1=0 input.xml

Output

  <SubtitleTracks>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>true</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>1</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
    <SubtitleTrack>
      <IsNotifying>true</IsNotifying>
      <Burned>false</Burned>
      <Default>false</Default>
      <Forced>false</Forced>
      <SourceTrack>
        <SourceId>0</SourceId>
        <TrackNumber>2</TrackNumber>
        <Language>English [VOBSUB]</Language>
        <LanguageCode>eng</LanguageCode>
        <SubtitleType>VobSub</SubtitleType>
      </SourceTrack>
      <SrtOffset>0</SrtOffset>
      <SubtitleType>VobSub</SubtitleType>
    </SubtitleTrack>
  </SubtitleTracks>
Dudi Boy
  • 4,551
  • 1
  • 15
  • 30