Simple trick to parse recursive XML using XML package?

592 views
Skip to first unread message

Chuck

unread,
Jul 14, 2010, 10:23:16 AM7/14/10
to golang-nuts
Is there a simple trick to defining an XML parser for recursive tags?
I get an "invalid recursive type NavPoint" when I try the obvious (to
me) type declaration:

type NavPoint struct {
XMLName xml.Name "navPoint";
Id string "attr";
PlayOrder string "attr";
NavLabel NavLabel;
Content Content;
NavPoint []NavPoint;
}

What I am trying to parse is this:

<navPoint id="np-3" playOrder="3">
<navLabel>
<text>CHAPTER I</text>
</navLabel>
<content
src="www.gutenberg.org@files@17...@17959-8-0.html#id00013"/>
</navPoint>
<navPoint id="np-4" playOrder="4">
<navLabel>
<text>CHAPTER II</text>
</navLabel>
<content
src="www.gutenberg.org@files@17...@17959-8-0.html#id00061"/>
<navPoint id="np-5" playOrder="5">
<navLabel>
<text>CHAPTER III</text>
</navLabel>
<content
src="www.gutenberg.org@files@17...@17959-8-0.html#id00085"/>
<navPoint id="np-6" playOrder="6">
<navLabel>
<text>CHAPTER IV</text>
</navLabel>
<content
src="www.gutenberg.org@files@17...@17959-8-0.html#id00173"/>
</navPoint>
</navPoint>
</navPoint>

Any suggestions?

Ian Lance Taylor

unread,
Jul 14, 2010, 10:34:20 AM7/14/10
to Chuck, golang-nuts
Chuck <char...@gmail.com> writes:

> Is there a simple trick to defining an XML parser for recursive tags?
> I get an "invalid recursive type NavPoint" when I try the obvious (to
> me) type declaration:
>
> type NavPoint struct {
> XMLName xml.Name "navPoint";
> Id string "attr";
> PlayOrder string "attr";
> NavLabel NavLabel;
> Content Content;
> NavPoint []NavPoint;
> }

Can you provide a complete example? When I expand that example with
definitions for NavLabel and Content, it compiles for me.

Also, there were some bugs in this area a couple of months ago. Are you
running the current release?

Ian

Chuck

unread,
Jul 14, 2010, 12:29:30 PM7/14/10
to golang-nuts
I am compiling using 8g version 5691 on OS X 10.6.3...

I'm grabbing the latest go distribution and compiling it now. (pause
for compile) Now 8g -V reports 5759.

Same error. I'll present a complete example shortly. Thanks for your
help! I'm working on an epub reader and some of those ebooks have
nested tables of contents. More in a bit...

Chuck

Chuck

unread,
Jul 14, 2010, 1:17:32 PM7/14/10
to golang-nuts
I've trimmed the program down to the following which exhibits the
behavior (on my computer at least.)

First, the input file (named rectest.ncx) :

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE ncx PUBLIC '-//NISO//DTD ncx 2005-1//EN'
'http://www.daisy.org/z3986/2005/ncx-2005-1.dtd'>

<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1"
xml:lang="en">
<navMap>
<navMap>
</ncx>


And the code that attempts to parse it (which I have named
rectest.go) :

package main

import (
"os"
"io"
"fmt"
"xml"
"strings" )

func main() {

type NavLabel struct { XMLName xml.Name "navLabel";
Text
string; }

type Content struct { XMLName xml.Name "content";
Src string "attr"; }

type NavPoint struct { XMLName xml.Name "navPoint";
Id string "attr";
PlayOrder string "attr";
NavLabel NavLabel;
Content Content;
// NavPoint []NavPoint;
}

type NavMap struct { XMLName xml.Name "navMap";
NavPoint
[]NavPoint; }

type Ncx struct { XMLName xml.Name "ncx";
Xmlns string "attr";
Version string "attr";
NavMap
NavMap; }

f, err := os.Open("rectest.ncx", os.O_RDONLY, 0);
if err != nil {
fmt.Printf("File Open Error\n")
return
}
defer f.Close();
d, err := f.Stat();
if err != nil {
fmt.Printf("File Stat Error\n")
return
}
buf := make([]byte, d.Size);
_, err = io.ReadFull(f, buf);
if err != nil {
fmt.Printf("File Read Error\n")
return
}

var eToc = Ncx{ NavMap: NavMap{ NavPoint: nil } }

xml.Unmarshal ( strings.NewReader(string(buf)) , &eToc )

fmt.Printf( "\nNavPoints:\n ")
for _,v:= range eToc.NavMap.NavPoint {
fmt.Printf( v.Id )
fmt.Printf( " - " )
fmt.Printf( v.PlayOrder)
fmt.Printf( " - " )
fmt.Printf( v.NavLabel.Text )
fmt.Printf( " - " )
fmt.Printf( v.Content.Src )
fmt.Printf( "\n " )
}

return
}

And for completeness sake, a Makefile:

include $(GOROOT)/src/Make.$(GOARCH)

ALL=rectest

all: $(ALL)

clean:
rm -rf *.[68] $(ALL)

%: %.go
$(GC) $*.go
$(LD) -o $@ $*.$O

When I compile with the comment in front of the recursive NavPoint in
place the compilation succeeds and I get the following output when
running the program:

host182-59:rectest cperkins$ make
8g rectest.go
8l -o rectest rectest.8
host182-59:rectest cperkins$ ./rectest

NavPoints:
np-3 - 3 - CHAPTER I - www.gutenberg.org@files@17...@17959-8-0.html#id00013
np-4 - 4 - CHAPTER II - www.gutenberg.org@files@17...@17959-8-0.html#id00061


When I compile with the comment in front of the recursive NavPoint
removed the compilation fails as follows:

host182-59:rectest cperkins$ make
8g rectest.go
rectest.go:24: invalid recursive type NavPoint
make: *** [rectest] Error 1


I hope I am doing something simple and stupid... it wouldn't be the
first time!

Many thanks,

Chuck

Rob 'Commander' Pike

unread,
Jul 14, 2010, 1:38:29 PM7/14/10
to Chuck, golang-nuts
You don't need nearly that much code to see the problem.

package main

func main() {
type X struct { x []X }
}

says "invalid recursive type X". What's interesting is that


package main
type X struct { x []X }

compiles. It's a compiler issue, no doubt, and I will record it.

-rob


Chuck

unread,
Jul 14, 2010, 1:43:34 PM7/14/10
to golang-nuts
I see, and moving the struct definitions out of the function and to
the top level has fixed my problem.

That was quick!

Chuck

Chuck

unread,
Jul 15, 2010, 5:57:17 PM7/15/10
to golang-nuts
... and now the CompilerBug has been fixed, go team go!

http://code.google.com/p/go/issues/detail?id=920

Chuck

David Leimbach

unread,
Jul 15, 2010, 6:01:38 PM7/15/10
to golang-nuts
Or is that "Go Go team! Go!" ?
Reply all
Reply to author
Forward
0 new messages