Trouble with writing an XML document

Hi. I’m using Applescript to create an XML file and so need to save it as text-only. While the resulting file appears to be a text-only file, it will not work. If I manually copy all the text into, say, a Word document and save that as text-only it does work. Obviously, I’m writing the file incorrectly. What is the correct way to do this? Here’s the part of my code that writes the file:

on write_to_file(this_data, target_file, append_data)
	try
		set the target_file to the target_file as text
		set the open_target_file to ¬
			open for access file target_file with write permission
		if append_data is false then ¬
			set eof of the open_target_file to 0
		write this_data to the open_target_file starting at eof as «class utf8»
		close access the open_target_file
		return true
	on error
		try
			close access file target_file
		end try
		return false
	end try
end write_to_file

Thanks, Michael

Hi Michael,

this is just the subroutine.
It’s more interesting how you call the routine and pass the parameters

Hi Stefan. Here’s the whole script (actually, you helped me with this one in a previous post)


property searchList : {" "}
property replaceList : {"_"}

--Set these variables
set thesePlaylists to {"Playlist 1", "Playlist 2", "Playlist 3", "Playlist 4", "Playlist 5"}
set lo_res_folder to "mp3_lo/"
set hi_res_folder to "mp3_hi/"
set aif_folder to "aif/"
set this_data to ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" & return as string) & return as string
set myFileName to "library.xml"
--end set these variables

set new_track_genre to ""
set new_track_subgenre to ""

set this_file to ((path to desktop folder as Unicode text) & myFileName)

my write_to_file(this_data, this_file, true)

repeat with thisPlaylist in thesePlaylists
	tell application "iTunes"
		file tracks of user playlist thisPlaylist
		
		repeat with thisTrack in result
			set track_name_c to my convertText(name of thisTrack)
			set track_name to name of thisTrack
			set track_genre to new_track_genre
			set new_track_genre to grouping of thisTrack
			set track_subgenre to new_track_subgenre
			set new_track_subgenre to genre of thisTrack
			set no_seconds to duration of thisTrack as number
			set no_minutes to no_seconds div 60
			set no_seconds to round (no_seconds mod 60)
			if no_seconds < 10 then
				set seconds_text to "0" & (no_seconds as string)
			else
				set seconds_text to no_seconds as string
			end if
			set trackDuration to (no_minutes as string) & ":" & seconds_text
			
			--write to TEXT file
			if new_track_genre = track_genre then
				if new_track_subgenre = track_subgenre then
					set this_data to tab & tab & "<track duration=\"" & trackDuration & "\" lo_res=\"" & lo_res_folder & track_name_c & "_lo.mp3\"" & " hi_res=\"" & hi_res_folder & track_name_c & ".mp3\"" & " aiff=\"" & aif_folder & track_name_c & ".aif\">" & track_name & "</track>" & return as string
				else
					set this_data to tab & "</subgenre>" & return & tab & "<subgenre title=\"" & new_track_subgenre & "\">" & return & tab & tab & "<track duration=\"" & trackDuration & "\" lo_res=\"" & lo_res_folder & track_name_c & "_lo.mp3\"" & " hi_res=\"" & hi_res_folder & track_name_c & ".mp3\"" & " aiff=\"" & aif_folder & track_name_c & ".aif\">" & track_name & "</track>" & return as string
				end if
			else
				if track_genre = "" then
					set this_data to "<genre title=\"" & new_track_genre & "\">" & return & tab & "<subgenre title=\"" & new_track_subgenre & "\">" & return & tab & tab & "<track duration=\"" & trackDuration & "\" lo_res=\"" & lo_res_folder & track_name_c & "_lo.mp3\"" & " hi_res=\"" & hi_res_folder & track_name_c & ".mp3\"" & " aiff=\"" & aif_folder & track_name_c & ".aif\">" & track_name & "</track>" & return as string
				else
					set this_data to tab & "</subgenre>" & return & "</genre>" & return & return & "<genre title=\"" & new_track_genre & "\">" & return & tab & "<subgenre title=\"" & new_track_subgenre & "\">" & return & tab & tab & "<track duration=\"" & trackDuration & "\" lo_res=\"" & lo_res_folder & track_name_c & "_lo.mp3\"" & " hi_res=\"" & hi_res_folder & track_name_c & ".mp3\"" & " aiff=\"" & aif_folder & track_name_c & ".aif\">" & track_name & "</track>" & return as string
				end if
			end if
			set this_file to ((path to desktop folder as Unicode text) & myFileName)
			my write_to_file(this_data, this_file, true)
		end repeat
	end tell
end repeat
set this_data to tab & "</subgenre>" & return & "</genre>" as string
set this_file to ((path to desktop folder as Unicode text) & myFileName)
my write_to_file(this_data, this_file, true)


on write_to_file(this_data, target_file, append_data)
	try
		set the target_file to the target_file as text
		set the open_target_file to ¬
			open for access file target_file with write permission
		if append_data is false then ¬
			set eof of the open_target_file to 0
		write this_data to the open_target_file starting at eof as «class utf8»
		close access the open_target_file
		return true
	on error
		try
			close access file target_file
		end try
		return false
	end try
end write_to_file

to switchText of t from s to r
	set text item delimiters to s
	set t to t's text items
	set text item delimiters to r
	tell t to set t to beginning & ({""} & rest)
	t
end switchText

to convertText(t)
	set d to text item delimiters
	considering case
		repeat with n from 1 to count searchList
			set t to switchText of t from my searchList's item n to my replaceList's item n
		end repeat
	end considering
	set text item delimiters to d
	t
end convertText

display dialog "Finished!" buttons {"Okay"} default button 1 with icon 1

I would prefer to concatenate all the data to one string and write finally the data to disk

btw:
Once defined as class string, you can (and should) omit all as-string-coercions later, because
AppleScript will take the class of the first text element and will coerce while concatenting the following ones automatically
The default text class is string, so this is sufficient for the header

set this_data to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" & return & return

Explain “will not work”.

My first guess was the carriage returns (ASCII character 13); I would expect line feeds (ASCII character 10) to be the proper character for an XML file. A quick check shows that Word is changing the returns to line feeds, so I believe that’s your (main*) problem.

I would add this near the top of your script:

-- Use a variable for line feeds so you only have to call `ASCII character` once
set lineFeed to ASCII character 10

Then replace all the (textual!) returns in your script with that variable.

  • Take out the needless as string coercions that Stefan pointed out.

Thanks Bruce & Stefan. Changing the returns worked. I made the other suggested changes as well.

Michael