Can XML files be parsed and data placed into arrays/Types?

Started by LordRhys, March 28, 2011, 03:04:41 PM

Previous topic - Next topic

LordRhys

Searched through the forums and there were no entries newer than 2009 where some members were attempting to make XML files readable in PlayBasic. Has this been done or has it been added as a feature? I'm trying to port some stuff i did in DBP which made use of XML files wher eI had all my data stored.

kevin


You could write an XML parser,  I can't recall anyone really bothering.   It'd just revolve around  loading the file into a string, then Instring() through it. 


LordRhys

Was looking at some DLL link examples, can I use a dll written for DBP and call the functions in Playbasic? I have all the call syntax for the dll i.e.

Load XML  xmlFile,xmlNo,ignoreWhitespace

use following:

LinkDLL "DBXML.dll"
   Load XML(xmlFile,xmlNo,ignoreWhitespace)  Alias "Load XML" as Integer
   XML Document Root(xmlNo) Alias "XML Document Root" as integer
   .
   .
   .
EndLinkDLL


kevin

Quotecan I use a dll written for DBP and call the functions in Playbasic?

 yes and no.   If the dll is written as a plug in, then nope (as it'll be internally hooking back into the Db engine, which obviously won't work from a PB environment), if it's not, then probably yes.



LordRhys

Does a dll have to be written in C++ or can it be written in .Net? My C++ skills are very weak but I can code in C# and VB.Net.

Big C.

I would say you can use any language to create your DLL. In my early project MediaToExa-PackMaker the provided DLL was written in PureBasic and called from PlayBasic Code.


kevin

Quote from: LordRhys on March 31, 2011, 09:48:32 AM
can it be written in .Net?

I wouldn't recommended it .. 

Why not show us what you're trying to parse ?

LordRhys

Attached is an XML file I use for the entities, below is an excerpt from my races xml file, In DBP there was an XML plugin I was able to use, If easier I could just put this into a database and read it. I do not read the whole file, my code reads an entity type xml based off a random number and then randomly pulls an entity from the attached file. The Races file below is parsed and the data is read in for Race selected on character creation.

<Races version="1.0">
<Race name="Human" id ="0">
<StrMin>5</StrMin>
  <StrMax>18</StrMax>
  <DexMin>5</DexMin>
  <DexMax>18</DexMax>
  <ConMin>4</ConMin>
  <ConMax>18</ConMax>
  <IntMin>5</IntMin>
  <IntMax>18</IntMax>
  <WisMin>5</WisMin>
  <WisMax>18</WisMax>
  <WPMin>6</WPMin>
  <WPMax>19</WPMax>
  <PercMin>4</PercMin>
  <PercMax>8</PercMax>
  <LKMin>4</LKMin>
  <LKMax>10</LKMax>
  <Bonus>0</Bonus> 
  <Resistance>0</Resistance>         
    <XPMod>1.0</XPMod>                 
    <Gold>0</Gold>             
    <LevelCap>20</LevelCap>
  </Race>
  <Race name="High Elf" id ="1">
<StrMin>5</StrMin>
  <StrMax>18</StrMax>
  <DexMin>5</DexMin>
  <DexMax>18</DexMax>
  <ConMin>4</ConMin>
  <ConMax>18</ConMax>
  <IntMin>5</IntMin>
  <IntMax>18</IntMax>
  <WisMin>5</WisMin>
  <WisMax>18</WisMax>
  <WPMin>6</WPMin>
  <WPMax>19</WPMax>
  <PercMin>4</PercMin>
  <PercMax>8</PercMax>
  <LKMin>4</LKMin>
  <LKMax>10</LKMax>
  <Bonus>0</Bonus> 
  <Resistance>0</Resistance>         
    <XPMod>1.0</XPMod>                 
    <Gold>0</Gold>             
    <LevelCap>20</LevelCap>
  </Race>
</Races>



kevin


The data doesn't seem to be nested (as in, has same ID inside of an ID), so you can use Instring searches to pull out the chunks that you want.   I wouldn't bother loading it partically, just load it all in memory and hack away.  It's a few K

For the race data find the position of "<Races" and "</Races"  - Then grab the text between them, so this is child text data of that type.

To get the race info, we skim through the child data chunk (of races) looking for the "<Race" "</Race>" - Grab the data between the two points them continue searching on the from the end tag position.

So each race has it's own child text data with it's field properties within it.   You could grab each one using the same instring process, or skim though them looking for fields, one by one.  The latter would give you a list of what fields are present, so you could do some error checking.  Although you'd find that by specifically searching anyway.   So either way would do.   



kevin

 Something like this.


Type tEnemyText
Type$
Category
TextChunk$ ;
EndType
Dim EnemyText(0) as tEnemyText


file$="Enemies.xml"

t=timer()
xml$=LoadToString(File$)
print len(xml$)
count=Parse_Enemies(Xml$)
print "Load Time:"+str$(timer()-t)

print "---------------"
print "enemy stuff"
print "---------------"
for lp=1 to Count
Display_enemy(lp)
next

sync
waitkey



Function Display_Enemy(index)
s$=str$(EnemyText(Index).Category)
s$+="="+EnemyText(Index).Type$
print s$
Endfunction



Function  Parse_Enemies(Xml$)

// Fetch the data between the open and close tags
Chunk$,Properties$,Position=XML_FetchChunk(Xml$,"Enemies",0)

// Found something
if Chunk$<>""

; Create the 2d array returned tags will be dropped in
Dim Tags$(2,2)

SearchPos=0
Repeat
ThisEnemy$,ThisEnemyProperties$,SearchPos=XML_FetchChunk(Chunk$,"Enemy",SearchPos)

if ThisEnemy$<>""
; Parse out the tags
Count=ParsePropertyFields(ThisEnemyProperties$,Tags$())
Index=GetFreeCell(EnemyText())
For lp=0 to Count-1
select lower$(Tags$(lp,1))
case "type"
EnemyText(Index).type$=Tags$(lp,2)
case "category"
EnemyText(Index).category$=val(Tags$(lp,2))
default
#print "unknown enemy field"
endselect
next
EnemyText(Index).textChunk$=ThisEnemy$

EnemyCount=Index
endif
until SearchPos<1


endif

EndFunction EnemyCount


Function  XML_FetchChunk(Xml$,Tag$,StartingPos=0)
// Assumes the tags are formatted <tag> data </tag>

OpeningTagPos=instring(xml$,"<"+tag$,StartingPos)
if OpeningTagPos>0
CloseOfTag=instring(xml$,">",OpeningTagPos)
if CLoseOfTag>OPeningTagPos
CLoseOfTag++
EndTag$="</"+tag$+">"
TagEndPos  = instring(Xml$,Endtag$,CLoseOfTag)
if TagEndPos>0

// Grab the raw chunk of the chracters
Chunk$=Mid$(Xml$,CloseOfTag,TagEndPos-CloseOfTag)

OpeningTagPos+=Len("<"+tag$)
Properties$=Mid$(Xml$,OpeningTagPos,(CloseOfTag-1)-OpeningTagPos)

// Start of next search
ContinuePos=TagEndPos+Len(EndTag$)
endif
endif
endif
EndFunction Chunk$, Properties$, ContinuePos


Function LoadToString(File$)
if FIleexist(file$)
size=filesize(file$)
f=readnewfile(file$)
Result$=readchr$(f,size)
closefile f
endif
EndFunction Result$



// Note This code assumes the parameters are in the
// following format  Param="1" Param="2"
 
Function ParsePropertyFields(ThisString$,FieldProperties$())
t$=ThisString$
pos=instring(t$," ",1,false)
IF pos>0

BufferSize=10

; Check if the array exists ? - if it does and is smaller, __AVOID___ some
; memory thrashing by not always creating the field properties array!
if GetArrayStatus(FieldProperties$())=true
Size=GetArrayElements(FieldProperties$(),1)
if Size<BufferSize
ReDim FieldProperties$(BufferSize,2)
else
BufferSize=Size
endif
else
ReDim FieldProperties$(BufferSize,2)
endif


LastPos=pos
EqualsPos=Lastpos+1
Repeat
EqualsPos=Instring(t$,"=",EqualsPos,false)
if EqualsPos>LastPos
NameTag$=trim$(mid$(t$,LastPos,EqualsPos-LastPos)," ")
ValueTag$=""
inc EqualsPos
Quote1Pos=Instring(t$,chr$(34),EqualsPos,false)
if Quote1Pos=>EqualsPos
inc Quote1Pos
Quote2Pos=Instring(t$,chr$(34),Quote1Pos,false)
if Quote2Pos>Quote1Pos
ValueTag$=mid$(t$,Quote1Pos,Quote2Pos-Quote1Pos)
LastPos =Quote2pos+1
EqualsPos=LastPos
endif
endif

FieldProperties$(FieldCount,1) =NameTag$
FieldProperties$(FieldCount,2) =ValueTag$
Inc FieldCount
If FieldCount>BufferSize
BufferSize=BufferSize+128
ReDim FieldProperties$(BufferSize,2)
endif

endif
until  EqualsPos<1

EndIF
EndFunction FieldCount



LordRhys

Thanks for the info Kevin, I'm just stepping into the PlayBasic waters so will have to ingest this information so I understand what I'm doing.