Unmarshal HTML nested in XML

Viewed 205

I am receiving an xml file from a 3rd party which has an HTML element within one of the XML tags. I cannot work out how to unmarshal this to get the href URL.

XML Example:

<SOME_HTML>
  <a href="http://www.google.com" target="_blank">
  google</a>
</SOME_HTML>

This is as far as I have got so far but not adding anything to the struct:

type Href struct {
    Link string `xml:"href"`
}
type Link struct {
    URL []Href `xml:"a"`
}
type XmlFile struct {
    HTMLTag []Link `xml:"SOME_HTML"`
}

myFile := []byte(`<?xml version="1.0" encoding="utf-8"?>
<SOME_HTML>
    <a href="http://www.google.com" target="_blank">
    google</a>
</SOME_HTML>`)

var output XmlFile
err := xml.Unmarshal(myFile, &output)
fmt.Println(output) // {[]}
3 Answers

You can do it like that (https://play.golang.org/p/MJzAVLBFfms):

type aElement struct {
    Href string `xml:"href,attr"`
}

type content struct {
    A aElement `xml:"a"`
}

func main() {
    test := `<SOME_HTML><a href="http://www.google.com" target="_blank">google</a></SOME_HTML>`

    var result content
    if err := xml.Unmarshal([]byte(test), &result); err != nil {
        log.Fatal(err)
    }
    fmt.Println(result)
}

Parsing everything in the xml, assuming also there could be more than one a tag in the html or other tags (like a div).

If this is not needed, just replace XmlFile.Links with XmlFile.Link of type Link (not []Link)

func main() {
    type Link struct {
        XMLName xml.Name `xml:"a"`
        URL     string   `xml:"href,attr"`
        Target  string   `xml:"target,attr"`
        Content string   `xml:",chardata"`
    }
    type Div struct {
        XMLName xml.Name `xml:"div"`
        Classes string   `xml:"class,attr"`
        Content string   `xml:",chardata"`
    }
    type XmlFile struct {
        XMLName xml.Name `xml:"SOME_HTML"`
        Links   []Link   `xml:"a"`
        Divs    []Div    `xml:"div"`
    }

    myFile := []byte(`<?xml version="1.0" encoding="utf-8"?>
<SOME_HTML>
    <a href="http://www.google.com" target="_blank">google</a>
    <a href="http://www.facebook.com" target="_blank">facebook</a>
    <div class="someclass">text</div>
</SOME_HTML>`)

    var output XmlFile
    err := xml.Unmarshal(myFile, &output)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(output)
}

Playground

Edit: Added more tags in the xml to show how to parse different tag types.

You can parse the example you posted using a regular XML parser, there are however a whole lot of exceptions to the XML syntax which are commonly accepted as valid HTML.

The simplest example I can think of is : all html interpreters I know of understand that <br> (an unclosed <br> tag) is the same as a self closing <br /> tag.

If you don't know how the HTML on the other end of the service is generated, you are better off using an HTML parser.

For example, there is the golang.go/x/net/html package which provides several functions to parse HTML :

https://play.golang.org/p/3hUogiwdRPO

func findFirstHref(n *html.Node, indent string) string {
    if n.Type == html.ElementNode {
        fmt.Println("  * scanning:" + indent + n.Data)
    }

    if n.Type == html.ElementNode && n.Data == "a" {
        for _, a := range n.Attr {
            if a.Key == "href" {
                return a.Val
            }
        }
    }
    for c := n.FirstChild; c != nil; c = c.NextSibling {
        href := findFirstHref(c, indent+"  ")
        if href != "" {
            return href
        }
    }
    return ""
}

func main() {
    doc1, err := html.Parse(strings.NewReader(sample1))
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("href in sample1:", findFirstHref(doc1, ""))
    }
    
    doc2, err := html.Parse(strings.NewReader(sample2))
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("href in sample2:", findFirstHref(doc2, ""))
    }
}

const (
    sample1 = `<?xml version="1.0" encoding="utf-8"?>
<SOME_HTML>
    <a href="http://www.google.com" target="_blank">
    google</a>
</SOME_HTML>`

    // sample2 is an invalid XML document (it has unclosed "<br>" tags):
    sample2 = `
    <p> line1 <br> line2
<a href="foobar" target="_blank">
  Some <br> text
</a>
</p>
`
)
Related