libxml external dtd

BlitzMax Forums/Brucey's Modules/libxml external dtd

Czar Flavius(Posted 2010) [#1]
Hi. I have some xml files which need to be validated with a seperate dtd file, but I can't get libxml to validate them correctly despite trying many variations. Here's the code I'm using to validate:
Function validate:Int(filename:String)
	Local reader:TxmlTextReader = TxmlTextReader.fromFile(filename)
	reader.setParserProp(3, 1)
	Local ret = reader.read()
	While ret = 1
		ret = reader.read()
	Wend
	reader.free()
	If ret <> 0 Then
		RuntimeError "Error validating " + filename
		Return False
	End If
	Return True
End Function


This is world.dtd
<!ELEMENT world (entities, players)>
<!ELEMENT entities (object*)>
<!ELEMENT object EMPTY>
<!ELEMENT players (player*)>
<!ELEMENT player EMPTY>

<!ATTLIST world x CDATA #REQUIRED>
<!ATTLIST world y CDATA #REQUIRED>
<!ATTLIST world background CDATA #REQUIRED>

<!ATTLIST object name CDATA #REQUIRED>
<!ATTLIST object x CDATA #REQUIRED>
<!ATTLIST object y CDATA #REQUIRED>
<!ATTLIST object angle CDATA "-1">
<!ATTLIST object player CDATA #IMPLIED>

<!ATTLIST player id CDATA #REQUIRED>
<!ATTLIST player money CDATA #REQUIRED>
<!ATTLIST player view_x CDATA #REQUIRED>
<!ATTLIST player view_y CDATA #REQUIRED>


This is a sample xml file
<!DOCTYPE world SYSTEM "world.dtd">

<world x="10000" y="10000" background="..\Data\Graphics\Scenery\Background\Grass1.png">
	<entities>
		<object name="Scenery:Rock" x="50" y="50" />
		<object name="Scenery:Stone" x="250" y="50" />
		<object name="Scenery:Tree" x="5000" y="2500" />
		<object name="Scenery:Tree" x="5500" y="4500" />
		<object name="Scenery:Tree" x="6060" y="2550" />
		<object name="Scenery:Tree" x="4050" y="2350" />
		<object name="Turret:Cannon" x="250" y="2850" angle="50" player="1" />
	</entities>
	<players>
		<player id="1" money="300" view_x="100" view_y="100" />
	</players>
</world>


If I place the dtd inline with the xml file, it works and validates perfectly. If I put it in a seperate file (in the same folder) it allows anything to work. Remove the x and y from world and it will still load for example.


Brucey(Posted 2010) [#2]
I've tried various things, and it always works as I'd expect.

If I change the sample.xml :
		<object name="Scenery:Stone"/><!-- x="250" y="50" /-->


I get this output :
sample.xml:6: element object: validity error : Element object does not carry attribute y
		<object name="Scenery:Stone"/><!-- x="250" y="50" /-->
		                              ^
sample.xml:6: element object: validity error : Element object does not carry attribute x
		<object name="Scenery:Stone"/><!-- x="250" y="50" /-->

which is correct.

If I rename the world.dtd file, I get this output :
sample.xml:1: I/O error : failed to load external entity "world.dtd"
<!DOCTYPE world SYSTEM "world.dtd">
                                   ^
sample.xml:3: validity error : Validation failed: no DTD found !
 x="10000" y="10000" background="..\Data\Graphics\Scenery\Background\Grass1.png"

which is also correct.

This is my mini test app :
SuperStrict

Framework bah.libxml

validate("sample.xml")

Function validate:Int(filename:String)
	Local reader:TxmlTextReader = TxmlTextReader.fromFile(filename)
	reader.setParserProp(XML_PARSER_VALIDATE, 1)
	Local ret:Int = reader.read()
	While ret = 1
		ret = reader.read()
	Wend
	reader.free()
	If ret <> 0 Then
		RuntimeError "Error validating " + filename
		Return False
	End If
	Return True
End Function

All the files are in the same folder.


Czar Flavius(Posted 2010) [#3]
How do you get it to output the errors? I just get the runetimeerror and then it closes.


Brucey(Posted 2010) [#4]
It should output to stderr as default, I believe.

However, you can also handle errors yourself by overriding the default error function :

SuperStrict

Framework bah.libxml
Import brl.standardio

xmlSetErrorFunction(xmlError)


validate("sample.xml")

Function validate:Int(filename:String)
	Local reader:TxmlTextReader = TxmlTextReader.fromFile(filename)
	reader.setParserProp(XML_PARSER_VALIDATE, 1)
	Local ret:Int = reader.read()
	While ret = 1
		ret = reader.read()
	Wend
	reader.free()
	If ret <> 0 Then
		RuntimeError "Error validating " + filename
		Return False
	End If
	Return True
End Function

Function xmlError(data:Object, error:TxmlError)
	Print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
	Print "Domain   : " + error.getErrorDomain()
	Print "Message  : " + error.getErrorMessage()
	Print "Code     : " + error.getErrorCode()
	Print "Level    : " + error.getErrorLevel()
	Print "Filename : " + error.getFilename()
	Print "Line     : " + error.getLine()
	Print "Column   : " + error.getColumn()
End Function