Skip to content

Commit ea1dcfa

Browse files
committed
Support multiple link tags in both rss channel, items and in the universal feed.
1 parent 26a6aec commit ea1dcfa

22 files changed

+220
-47
lines changed

go.mod

+17-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
module github.com/mmcdole/gofeed
22

3-
go 1.14
3+
go 1.19
44

55
require (
6-
github.com/PuerkitoBio/goquery v1.5.1
7-
github.com/json-iterator/go v1.1.10
8-
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf
6+
github.com/PuerkitoBio/goquery v1.8.0
7+
github.com/json-iterator/go v1.1.12
8+
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354
99
github.com/stretchr/testify v1.3.0
1010
github.com/urfave/cli v1.22.3
11-
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
12-
golang.org/x/text v0.3.2
11+
golang.org/x/net v0.4.0
12+
golang.org/x/text v0.5.0
13+
)
14+
15+
require (
16+
github.com/andybalholm/cascadia v1.3.1 // indirect
17+
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
18+
github.com/davecgh/go-spew v1.1.1 // indirect
19+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
20+
github.com/modern-go/reflect2 v1.0.2 // indirect
21+
github.com/pmezard/go-difflib v1.0.0 // indirect
22+
github.com/russross/blackfriday/v2 v2.0.1 // indirect
23+
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
1324
)

go.sum

+21-20
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2-
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
3-
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
4-
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
5-
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
2+
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
3+
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
4+
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
5+
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
66
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
77
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
88
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
99
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1010
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1111
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
12-
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
13-
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
14-
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
15-
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
16-
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
12+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
13+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
14+
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 h1:Z6i7ND25ixRtXFBylIUggqpvLMV1I15yprcqMVB7WZA=
15+
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
1716
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
18-
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
19-
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
17+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
18+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
19+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
20+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
2021
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2122
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2223
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
@@ -28,15 +29,15 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
2829
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
2930
github.com/urfave/cli v1.22.3 h1:FpNT6zq26xNpHZy08emi755QwzLPs6Pukqjlc7RfOMU=
3031
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
31-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
32-
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
33-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
34-
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
35-
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
36-
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
37-
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
38-
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
39-
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
32+
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
33+
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
34+
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
35+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
36+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
37+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
38+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
39+
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
40+
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
4041
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
4142
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4243
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

rss/feed.go

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
type Feed struct {
1212
Title string `json:"title,omitempty"`
1313
Link string `json:"link,omitempty"`
14+
Links []string `json:"links,omitempty"`
1415
Description string `json:"description,omitempty"`
1516
Language string `json:"language,omitempty"`
1617
Copyright string `json:"copyright,omitempty"`
@@ -46,6 +47,7 @@ func (f Feed) String() string {
4647
type Item struct {
4748
Title string `json:"title,omitempty"`
4849
Link string `json:"link,omitempty"`
50+
Links []string `json:"links,omitempty"`
4951
Description string `json:"description,omitempty"`
5052
Content string `json:"content,omitempty"`
5153
Author string `json:"author,omitempty"`

rss/parser.go

+26-5
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ func (rp *Parser) parseRoot(p *xpp.XMLPullParser) (*Feed, error) {
118118
}
119119

120120
func (rp *Parser) parseChannel(p *xpp.XMLPullParser) (rss *Feed, err error) {
121-
122121
if err = p.Expect(xpp.StartTag, "channel"); err != nil {
123122
return nil, err
124123
}
@@ -128,6 +127,7 @@ func (rp *Parser) parseChannel(p *xpp.XMLPullParser) (rss *Feed, err error) {
128127

129128
extensions := ext.Extensions{}
130129
categories := []*Category{}
130+
links := []string{}
131131

132132
for {
133133
tok, err := rp.base.NextTag(p)
@@ -162,11 +162,12 @@ func (rp *Parser) parseChannel(p *xpp.XMLPullParser) (rss *Feed, err error) {
162162
}
163163
rss.Description = result
164164
} else if name == "link" {
165-
result, err := shared.ParseText(p)
165+
result, err := rp.parseLink(p)
166166
if err != nil {
167167
return nil, err
168168
}
169169
rss.Link = result
170+
links = append(links, result)
170171
} else if name == "language" {
171172
result, err := shared.ParseText(p)
172173
if err != nil {
@@ -295,6 +296,10 @@ func (rp *Parser) parseChannel(p *xpp.XMLPullParser) (rss *Feed, err error) {
295296
rss.Categories = categories
296297
}
297298

299+
if len(links) > 0 {
300+
rss.Links = links
301+
}
302+
298303
if len(extensions) > 0 {
299304
rss.Extensions = extensions
300305

@@ -311,7 +316,6 @@ func (rp *Parser) parseChannel(p *xpp.XMLPullParser) (rss *Feed, err error) {
311316
}
312317

313318
func (rp *Parser) parseItem(p *xpp.XMLPullParser) (item *Item, err error) {
314-
315319
if err = p.Expect(xpp.StartTag, "item"); err != nil {
316320
return nil, err
317321
}
@@ -320,6 +324,7 @@ func (rp *Parser) parseItem(p *xpp.XMLPullParser) (item *Item, err error) {
320324
extensions := ext.Extensions{}
321325
categories := []*Category{}
322326
enclosures := []*Enclosure{}
327+
links := []string{}
323328

324329
for {
325330
tok, err := rp.base.NextTag(p)
@@ -363,11 +368,12 @@ func (rp *Parser) parseItem(p *xpp.XMLPullParser) (item *Item, err error) {
363368
item.Content = result
364369
}
365370
} else if name == "link" {
366-
result, err := shared.ParseText(p)
371+
result, err := rp.parseLink(p)
367372
if err != nil {
368373
return nil, err
369374
}
370375
item.Link = result
376+
links = append(links, result)
371377
} else if name == "author" {
372378
result, err := shared.ParseText(p)
373379
if err != nil {
@@ -437,6 +443,10 @@ func (rp *Parser) parseItem(p *xpp.XMLPullParser) (item *Item, err error) {
437443
item.Categories = categories
438444
}
439445

446+
if len(links) > 0 {
447+
item.Links = links
448+
}
449+
440450
if len(extensions) > 0 {
441451
item.Extensions = extensions
442452

@@ -456,6 +466,18 @@ func (rp *Parser) parseItem(p *xpp.XMLPullParser) (item *Item, err error) {
456466
return item, nil
457467
}
458468

469+
func (rp *Parser) parseLink(p *xpp.XMLPullParser) (url string, err error) {
470+
href := p.Attribute("href")
471+
url, err = shared.ParseText(p)
472+
if err != nil {
473+
return
474+
}
475+
if url == "" && href != "" {
476+
url = href
477+
}
478+
return url, err
479+
}
480+
459481
func (rp *Parser) parseSource(p *xpp.XMLPullParser) (source *Source, err error) {
460482
if err = p.Expect(xpp.StartTag, "source"); err != nil {
461483
return nil, err
@@ -590,7 +612,6 @@ func (rp *Parser) parseGUID(p *xpp.XMLPullParser) (guid *GUID, err error) {
590612
}
591613

592614
func (rp *Parser) parseCategory(p *xpp.XMLPullParser) (cat *Category, err error) {
593-
594615
if err = p.Expect(xpp.StartTag, "category"); err != nil {
595616
return nil, err
596617
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"link": "http://example.org",
3+
"links": [ "http://example.org" ],
34
"items": [],
45
"version": "1.0"
56
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"link": "http://example3.org",
3+
"links": ["http://example.org", "http://example2.org", "http://example3.org"],
4+
"items": [],
5+
"version": "1.0"
6+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!--
2+
Description: rdf channel links
3+
-->
4+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/">
5+
<channel rdf:about="http://example.org/index.rdf">
6+
<link>http://example.org</link>
7+
<link>http://example2.org</link>
8+
<link href="http://example3.org" />
9+
</channel>
10+
</rdf:RDF>

testdata/parser/rss/rdf_item_link.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"items": [
33
{
4-
"link": "http://example.org"
4+
"link": "http://example.org",
5+
"links": [ "http://example.org" ]
56
}
67
],
78
"version": "1.0"
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"items": [
3+
{
4+
"link": "http://example3.org",
5+
"links": [
6+
"http://example.org",
7+
"http://example2.org",
8+
"http://example3.org"
9+
]
10+
}
11+
],
12+
"version": "1.0"
13+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!--
2+
Description: rdf item links
3+
-->
4+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/">
5+
<channel rdf:about="http://example.org/index.rdf">
6+
<items>
7+
<rdf:Seq>
8+
<rdf:li resource="http://example.org/entry/1" />
9+
</rdf:Seq>
10+
</items>
11+
</channel>
12+
<item rdf:about="http://example.org/entry/1">
13+
<link>http://example.org</link>
14+
<link>http://example2.org</link>
15+
<link href="http://example3.org" />
16+
</item>
17+
</rdf:RDF>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"items": [
3+
{
4+
"link": "http://example3.org",
5+
"links": [
6+
"http://example.org",
7+
"http://example2.org",
8+
"http://example3.org"
9+
]
10+
}
11+
],
12+
"version": "2.0"
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!--
2+
Description: rss item link
3+
-->
4+
<rss version="2.0">
5+
<channel>
6+
<item>
7+
<link>http://example.org</link>
8+
<link>http://example2.org</link>
9+
<link href="http://example3.org" />
10+
</item>
11+
</channel>
12+
</rss>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"link": "http://example.org",
3+
"links": [ "http://example.org" ],
34
"items": [],
45
"version": "2.0"
56
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"link": "http://example3.org",
3+
"links": ["http://example.org", "http://example2.org", "http://example3.org"],
4+
"items": [],
5+
"version": "2.0"
6+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!--
2+
Description: rss channel links
3+
-->
4+
<rss version="2.0">
5+
<channel>
6+
<link>http://example.org</link>
7+
<link>http://example2.org</link>
8+
<link href="http://example3.org" />
9+
</channel>
10+
</rss>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"feedType": "rss",
3+
"feedVersion": "2.0",
4+
"items": [
5+
{
6+
"link": "http://example3.org",
7+
"links": ["http://example.org", "http://example2.org", "http://example3.org"]
8+
}
9+
]
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!--
2+
Description: item link
3+
-->
4+
<rss version="2.0">
5+
<channel>
6+
<item>
7+
<link>http://example.org</link>
8+
<link>http://example2.org</link>
9+
<link href="http://example3.org" />
10+
</item>
11+
</channel>
12+
</rss>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"feedType": "rss",
3+
"feedVersion": "1.0",
4+
"items": [],
5+
"link": "http://example3.org",
6+
"links": ["http://example.org", "http://example2.org", "http://example3.org"]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!--
2+
Description: rdf channel link(s)
3+
-->
4+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/">
5+
<channel rdf:about="http://example.org/index.rdf">
6+
<link>http://example.org</link>
7+
<link>http://example2.org</link>
8+
<link href="http://example3.org" />
9+
</channel>
10+
</rdf:RDF>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"feedType": "rss",
3+
"feedVersion": "2.0",
4+
"items": [],
5+
"link": "http://example3.org",
6+
"links": ["http://example.org", "http://example2.org", "http://example3.org"]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!--
2+
Description: normal link(s)
3+
-->
4+
<rss version="2.0">
5+
<channel>
6+
<link>http://example.org</link>
7+
<link>http://example2.org</link>
8+
<link href="http://example3.org" />
9+
</channel>
10+
</rss>

0 commit comments

Comments
 (0)