XML - SAX reader

Hello everyone,

i have a question for you all. I'm using a SAX reader to import my xml-file, but i want to know the number of element i'm into. A little example explains:

root
element1
subelement1.1
subelement1.2
element2
subelement2.1

so if i'm in root, i want to get the number 1, when i'm in an element i want to get number 2, when i'm in a subelement i want to get number 3, ...

is their a way to get that?

thanks in advance
 

parul

Member
IMO, SAX is serial reader. It does not have the document in memory so it cannot tell you anything about what is going to come.

That is the reason why it can read invalid XMLs also.

If you have to do what you say, then it is better to use DOM parser.

BTW what are you trying to accomplish?

-Parul.
 
Well, I have an xml file but its not build correctly. The person who made it used equal names for different levels. And I need those information to place it in a temp-table.

for example

<brands>
<section>
<description> </description>
<image>
<description> </description>
</image>
</section>
<image>
<description> ...
</image>
</brands>

do you understand what i'm trying to say. He used equal names for different nodes.

so i want to now in what level of node i'm in to. 'cause the way i do it now he only saves the last description.

or can i solve it another way?
 

parul

Member
I think I know what you mean.

The StartElement Procedure has an input parameter
localname (I think it is the second parameter).

You have to keep track of this parameter, it can tell you what was the last element parsed.

-Parul.
 

tamhas

ProgressTalk.com Sponsor
All you should need is a couple of variables defined at the level of the handler. When you hit the StartElement, update a variable to indicate what is currently being processed and then when you hit the corresponding EndElement, bump it back up a level. Then, you can have a counter which you increment for the sequence. You could do this either with a single character variable in the form of 1.1.1, where you bump the right value (using entry) in the StartElement and truncate in EndElement or you could use one counter per level.
 
I know what you're trying to do, but its very difficult because there are many levels. I made a sort of schema from my xml

1. Brands
1.1. Section:
1.1.1. Formats
1.1.2. Illustrationlink
1.1.3. Description
1.1.4. Section:
1.1.4.1.description
1.1.4.2.illustrationlink
1.1.4.3.model ( attribute "id"):
1.1.4.3.1. modellink
1.1.4.3.2. illustrationlink
1.1.4.3.3. description
1.1.4.4.section:
1.1.4.4.1. description
1.1.4.4.2. document:
1.1.4.4.2.1.documentlink
1.1.4.4.2.2.descripton
1.1.4.4.3. section:
1.1.4.4.3.1.description
1.1.4.4.3.2.section:
1.1.4.4.3.2.1. description
1.1.4.4.3.2.2. section:
1.1.4.4.3.2.2.1.description
1.1.4.4.3.2.2.2.document:
1.1.4.4.3.2.2.2.1. documentlink
1.1.4.4.3.2.2.2.2. description
1.1.4.4.3.2.3. document:
1.1.4.4.3.2.3.1.documentlink
1.1.4.4.3.2.3.2.description

like you can see it has many levels with the same names. some sublevels are more then 1 time in another level. for example, a section can contain more then 1 models.

maybe someone can help me with the 1ste thing i want to do. i want to put each model where the modellink exist in a temp-table. Therefor i need the description of the section, a attribute of model ( called "id" ) and the value of modellink. ( the red ones ).

i used a sample of progress for the saxreader:

define temp-table ttBrands
fields ...

DEFINE VARIABLE hBuf AS HANDLE.
DEFINE VARIABLE hDBFld AS HANDLE.
DEFINE VARIABLE currentFieldValue AS CHARACTER.
DEFINE VARIABLE iProcessingState AS INTEGER.

hBuf = BUFFER ttBrands:HANDLE.

&SCOPED-DEFINE READY-TO-START 1
&SCOPED-DEFINE GETTING-RECORDS 2
&SCOPED-DEFINE GETTING-FIELDS 3
&SCOPED-DEFINE DONE 4

PROCEDURE StartDocument:
iProcessingState = {&READY-TO-START}.
END.

PROCEDURE StartElement:
DEFINE INPUT PARAMETER namespaceURI AS CHARACTER.
DEFINE INPUT PARAMETER localName AS CHARACTER.
DEFINE INPUT PARAMETER qName AS CHARACTER.
DEFINE INPUT PARAMETER attributes AS HANDLE.

IF qName = "Brand" THEN
iProcessingState = {&GETTING-RECORDS}.
ELSE IF qName = "Section" THEN DO:
iProcessingState = {&GETTING-FIELDS}.
CREATE ttBrands.
END.
ELSE IF iProcessingState = {&GETTING-FIELDS} THEN DO:
hDBFld = hBuf:BUFFER-FIELD(qName).
currentFieldValue = "".
END.
END.

PROCEDURE Characters:
DEFINE INPUT PARAMETER charData AS MEMPTR.
DEFINE INPUT PARAMETER numChars AS INTEGER.

currentFieldValue =
currentFieldValue + GET-STRING(charData, 1, GET-SIZE(charData)).
END.

PROCEDURE EndElement:
DEFINE INPUT PARAMETER namespaceURI AS CHARACTER.
DEFINE INPUT PARAMETER localName AS CHARACTER.
DEFINE INPUT PARAMETER qName as CHARACTER.

IF localName = "Brand" THEN
iProcessingState = {&DONE}.
ELSE IF localName = "Section" THEN
iProcessingState = {&GETTING-FIELDS}.
ELSE IF iProcessingState = {&GETTING-FIELDS} THEN
hDBFld:BUFFER-VALUE = currentFieldValue.
END.

PROCEDURE EndDocument:
FOR EACH ttbrands:
display ttBrands.
END.
RUN Cleanup.
END.

PROCEDURE FatalError:
DEFINE INPUT PARAMETER errMessage AS CHARACTER.
SELF:pRIVATE-DATA = "FATAL".
RUN Cleanup.
RETURN ERROR errMessage
+ "(Line " + STRING(SELF:LOCATOR-LINE-NUMBER)
+ ", Col " + STRING(SELF:LOCATOR-COLUMN-NUMBER)
+ ")".
END.

PROCEDURE Cleanup:
hBuf:EMPTY-TEMP-TABLE( ).
END.
 

tamhas

ProgressTalk.com Sponsor
In terms of handling the levels, I think the problem is simpler than you think. Every time you hit a new StartElement, you go down a level. Every time you hit an EndElement, you go back up a level. Two StartElements in a row, down two levels. Regardless of the element name.
 
Ok thx, i understand it now ;-)

but now I have another problem. When i look at my records (ttBrands ), he only "saves" the last model if there are more then 1 in a section.
I think I created my ttBrands on a wrong location... does someone now how I can solve this?

thanks in advance
 
Top