Marked as: Advanced
Hi,
here's my new and improved "do-it-all" xml parser function...
Please do test it to see if it behave well or not. I've tested it quite a lot myself, but would like to have it confirmed that it doesn't barf on something and possibly can cope with faulty xml-structures as well.
It doesn't contain a feature to traverse the structure as RobertM's version, but adding a function like that shouldn't be to difficult on top of it.
/* Test XML Parsing */
XMLTAG = '<maintag color="blue">'
XMLTAG = XMLTAG||' <sometag style="position: absolute">1'
XMLTAG = XMLTAG||' <anothertag border="0">2'
XMLTAG = XMLTAG||' <notag style="Postion: static" />'
XMLTAG = XMLTAG||' <sometag alt="" href="http://www.test.com">3'
XMLTAG = XMLTAG||' <sometag src="red.gif">4</sometag>'
XMLTAG = XMLTAG||' <sometag style="border: solid">5</sometag>'
XMLTAG = XMLTAG||' <sometag colspan="2" />'
XMLTAG = XMLTAG||' <sometag rowspan="3" />'
XMLTAG = XMLTAG||' <sometag>6</sometag>'
XMLTAG = XMLTAG||' <sometag border="5">7</sometag>'
XMLTAG = XMLTAG||' </sometag>'
XMLTAG = XMLTAG||' </anothertag>'
XMLTAG = XMLTAG||' <sometag style="position: absolute">8</sometag>'
XMLTAG = XMLTAG||' </sometag>'
XMLTAG = XMLTAG||' <notag />'
XMLTAG = XMLTAG||'</maintag>'
say '#'||parse_xml_tags( XMLTAG, 'notag',, 1 )||'#' /* */
say '#'||parse_xml_tags( XMLTAG, 'sometag', 'style=' )||'#' /* 1<anothertag border="0">2<sometag alt="" href="http://www.test.com">3<sometag src="red.gif">4</sometag><sometag style="border: solid">5</sometag><sometag colspan="2" /><sometag>6</sometag><sometag border="5">7</sometag></sometag></anothertag><sometag style="position: absolute">8</sometag> */
say '#'||parse_xml_tags( XMLTAG, 'sometag', 'style=', 1 )||'#' /* 1 */ /* (The first match) */
say '#'||parse_xml_tags( XMLTAG, 'sometag', 'style=', 2 )||'#' /* "position: absolute" */
say '#'||parse_xml_tags( XMLTAG, 'sometag', 'src=' )||'#' /* 4 */
say '#'||parse_xml_tags( XMLTAG, 'sometag', 'href=' )||'#' /* 3<sometag src="red.gif">4</sometag><sometag style="border: solid">5</sometag><sometag colspan="2" /><sometag>6</sometag><sometag border="5">7</sometag> */
say '#'||parse_xml_tags( XMLTAG, 'sometag', 'colspan=', 1 )||'#' /* */
say '#'||parse_xml_tags( XMLTAG, 'anothertag', 'colspan=', 2 )||'#' /* "2" */
retval = parse_xml_tags( XMLTAG, 'sometag', 'alt=' )
say '#'||parse_xml_tags( retval, 'sometag', 'style', 1 )||'#' /* 5 */
RETURN 0
parse_xml_tags: PROCEDURE /* xmlString, tag <<, tag_value>, selection> */
PARSE UPPER ARG xmlString, tag, val, selection
start_tag = '<'||tag
PARSE VALUE xmlString WITH pre(start_tag)(val)extra'>'post
SELECT
WHEN selection = 1 THEN /* tag */
DO
IF LENGTH( extra ) > 0 THEN
DO
PARSE VALUE SUBSTR( ARG(1), ABS( LENGTH( xmlString ) - LENGTH( post ) + 1 ) ) WITH pre'<'.
RETURN pre
END
END
WHEN selection = 2 THEN /* tag value */
RETURN SUBSTR( ARG(1), ABS( LENGTH( xmlString ) - LENGTH( post ) - LENGTH( extra ) ), LENGTH( extra ) - ( RIGHT( extra, 1 ) = '/' ) )
WHEN RIGHT( extra, 1 ) <> '/' THEN
DO
end_tag = '</'||tag||'>'
next = LENGTH( xmlString ) - LENGTH( post ) + 1
open_tag = next
PARSE VALUE post WITH pre(end_tag)post
close_tag = LENGTH( xmlString ) - LENGTH( post ) - LENGTH( end_tag )
count = COUNTSTR( start_tag, xmlString, open_tag, close_tag )
DO WHILE count > 0
next = close_tag + LENGTH( end_tag )
close_tag = POS( end_tag, xmlString, next )
IF close_tag = 0 THEN
close_tag = LENGTH( xmlString )
post = SUBSTR( xmlString, next, close_tag - next )
PARSE VALUE post WITH .(start_tag)extra'/>'post
DO WHILE LENGTH( post ) > 0
next = close_tag - LENGTH( post )
PARSE VALUE post WITH .(start_tag)extra'/>'post
END
count = count + COUNTSTR( start_tag, xmlString, next, close_tag ) - 1
END
IF close_tag = 0 THEN close_tag = LENGTH( ARG(1) ) - open_tag
ELSE close_tag = close_tag - open_tag
RETURN SUBSTR( ARG(1), open_tag, MAX( 1, close_tag ) )
END
OTHERWISE NOP
END
RETURN ''
COUNTSTR: PROCEDURE /* needle, haystack< <, startpos>, endpos> */
IF ARG() < 2 THEN RETURN -1
IF DATATYPE( ARG(3), 'W' ) THEN
next = ARG(3)
ELSE
next = 1
needle = ARG(1)
haystack = ARG(2)
IF DATATYPE( ARG(4), 'W' ) THEN
haystack = SUBSTR( haystack, next, ABS( ARG(4) - next ) )
next = 1
count = 0
DO WHILE next > 0
next = POS( needle, haystack, next )
IF next > 0 THEN DO
next = next + LENGTH( needle )
count = count + 1
END
END
RETURN count
//Jan-Erik