diff options
author | Paul van Brouwershaven <[email protected]> | 2021-12-02 17:30:36 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-12-02 17:30:36 +0100 |
commit | 0eaaa8fee37068bfc8ecfb760f770ecc9a7af22a (patch) | |
tree | 95cf7c5ac3a7e56c0eb411a28cae5c0412a510bd /parser | |
parent | 58adbeef88ea5c8769d12ba27eef2d89bdf575eb (diff) | |
download | hugo-0eaaa8fee37068bfc8ecfb760f770ecc9a7af22a.tar.gz hugo-0eaaa8fee37068bfc8ecfb760f770ecc9a7af22a.zip |
Implement XML data support
Example:
```
{{ with resources.Get "https://example.com/rss.xml" | transform.Unmarshal }}
{{ range .channel.item }}
<strong>{{ .title | plainify | htmlUnescape }}</strong><br />
<p>{{ .description | plainify | htmlUnescape }}</p>
{{ $link := .link | plainify | htmlUnescape }}
<a href="{{ $link }}">{{ $link }}</a><br />
<hr>
{{ end }}
{{ end }}
```
Closes #4470
Diffstat (limited to 'parser')
-rw-r--r-- | parser/frontmatter.go | 9 | ||||
-rw-r--r-- | parser/metadecoders/decoder.go | 20 | ||||
-rw-r--r-- | parser/metadecoders/decoder_test.go | 55 | ||||
-rw-r--r-- | parser/metadecoders/format.go | 16 | ||||
-rw-r--r-- | parser/metadecoders/format_test.go | 3 |
5 files changed, 99 insertions, 4 deletions
diff --git a/parser/frontmatter.go b/parser/frontmatter.go index 79701a0fc..a99829521 100644 --- a/parser/frontmatter.go +++ b/parser/frontmatter.go @@ -23,6 +23,8 @@ import ( toml "github.com/pelletier/go-toml/v2" yaml "gopkg.in/yaml.v2" + + xml "github.com/clbanning/mxj/v2" ) const ( @@ -62,7 +64,14 @@ func InterfaceToConfig(in interface{}, format metadecoders.Format, w io.Writer) _, err = w.Write([]byte{'\n'}) return err + case metadecoders.XML: + b, err := xml.AnyXmlIndent(in, "", "\t", "root") + if err != nil { + return err + } + _, err = w.Write(b) + return err default: return errors.New("unsupported Format provided") } diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go index 168c130ed..f0dcb0856 100644 --- a/parser/metadecoders/decoder.go +++ b/parser/metadecoders/decoder.go @@ -24,6 +24,7 @@ import ( "github.com/gohugoio/hugo/common/herrors" "github.com/niklasfasching/go-org/org" + xml "github.com/clbanning/mxj/v2" toml "github.com/pelletier/go-toml/v2" "github.com/pkg/errors" "github.com/spf13/afero" @@ -135,6 +136,25 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v interface{}) error { err = d.unmarshalORG(data, v) case JSON: err = json.Unmarshal(data, v) + case XML: + var xmlRoot xml.Map + xmlRoot, err = xml.NewMapXml(data) + + var xmlValue map[string]interface{} + if err == nil { + xmlRootName, err := xmlRoot.Root() + if err != nil { + return toFileError(f, errors.Wrap(err, "failed to unmarshal XML")) + } + xmlValue = xmlRoot[xmlRootName].(map[string]interface{}) + } + + switch v := v.(type) { + case *map[string]interface{}: + *v = xmlValue + case *interface{}: + *v = xmlValue + } case TOML: err = toml.Unmarshal(data, v) case YAML: diff --git a/parser/metadecoders/decoder_test.go b/parser/metadecoders/decoder_test.go index e0990a5f7..8cd5513b2 100644 --- a/parser/metadecoders/decoder_test.go +++ b/parser/metadecoders/decoder_test.go @@ -20,6 +20,59 @@ import ( qt "github.com/frankban/quicktest" ) +func TestUnmarshalXML(t *testing.T) { + c := qt.New(t) + + xmlDoc := `<?xml version="1.0" encoding="utf-8" standalone="yes"?> + <rss version="2.0" + xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Example feed</title> + <link>https://example.com/</link> + <description>Example feed</description> + <generator>Hugo -- gohugo.io</generator> + <language>en-us</language> + <copyright>Example</copyright> + <lastBuildDate>Fri, 08 Jan 2021 14:44:10 +0000</lastBuildDate> + <atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/> + <item> + <title>Example title</title> + <link>https://example.com/2021/11/30/example-title/</link> + <pubDate>Tue, 30 Nov 2021 15:00:00 +0000</pubDate> + <guid>https://example.com/2021/11/30/example-title/</guid> + <description>Example description</description> + </item> + </channel> + </rss>` + + expect := map[string]interface{}{ + "-atom": "http://www.w3.org/2005/Atom", "-version": "2.0", + "channel": map[string]interface{}{ + "copyright": "Example", + "description": "Example feed", + "generator": "Hugo -- gohugo.io", + "item": map[string]interface{}{ + "description": "Example description", + "guid": "https://example.com/2021/11/30/example-title/", + "link": "https://example.com/2021/11/30/example-title/", + "pubDate": "Tue, 30 Nov 2021 15:00:00 +0000", + "title": "Example title"}, + "language": "en-us", + "lastBuildDate": "Fri, 08 Jan 2021 14:44:10 +0000", + "link": []interface{}{"https://example.com/", map[string]interface{}{ + "-href": "https://example.com/feed.xml", + "-rel": "self", + "-type": "application/rss+xml"}}, + "title": "Example feed", + }} + + d := Default + + m, err := d.Unmarshal([]byte(xmlDoc), XML) + c.Assert(err, qt.IsNil) + c.Assert(m, qt.DeepEquals, expect) + +} func TestUnmarshalToMap(t *testing.T) { c := qt.New(t) @@ -38,6 +91,7 @@ func TestUnmarshalToMap(t *testing.T) { {"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]interface{}{"a": "Easy!", "b": map[string]interface{}{"c": 2, "d": []interface{}{3, 4}}}}, {"a:\n true: 1\n false: 2", YAML, map[string]interface{}{"a": map[string]interface{}{"true": 1, "false": 2}}}, {`{ "a": "b" }`, JSON, expect}, + {`<root><a>b</a></root>`, XML, expect}, {`#+a: b`, ORG, expect}, // errors {`a = b`, TOML, false}, @@ -72,6 +126,7 @@ func TestUnmarshalToInterface(t *testing.T) { {`#+DATE: <2020-06-26 Fri>`, ORG, map[string]interface{}{"date": "2020-06-26"}}, {`a = "b"`, TOML, expect}, {`a: "b"`, YAML, expect}, + {`<root><a>b</a></root>`, XML, expect}, {`a,b,c`, CSV, [][]string{{"a", "b", "c"}}}, {"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]interface{}{"a": "Easy!", "b": map[string]interface{}{"c": 2, "d": []interface{}{3, 4}}}}, // errors diff --git a/parser/metadecoders/format.go b/parser/metadecoders/format.go index bba89dbea..d34a261bf 100644 --- a/parser/metadecoders/format.go +++ b/parser/metadecoders/format.go @@ -30,6 +30,7 @@ const ( TOML Format = "toml" YAML Format = "yaml" CSV Format = "csv" + XML Format = "xml" ) // FormatFromString turns formatStr, typically a file extension without any ".", @@ -51,6 +52,8 @@ func FormatFromString(formatStr string) Format { return ORG case "csv": return CSV + case "xml": + return XML } return "" @@ -68,27 +71,32 @@ func FormatFromMediaType(m media.Type) Format { return "" } -// FormatFromContentString tries to detect the format (JSON, YAML or TOML) +// FormatFromContentString tries to detect the format (JSON, YAML, TOML or XML) // in the given string. // It return an empty string if no format could be detected. func (d Decoder) FormatFromContentString(data string) Format { csvIdx := strings.IndexRune(data, d.Delimiter) jsonIdx := strings.Index(data, "{") yamlIdx := strings.Index(data, ":") + xmlIdx := strings.Index(data, "<") tomlIdx := strings.Index(data, "=") - if isLowerIndexThan(csvIdx, jsonIdx, yamlIdx, tomlIdx) { + if isLowerIndexThan(csvIdx, jsonIdx, yamlIdx, xmlIdx, tomlIdx) { return CSV } - if isLowerIndexThan(jsonIdx, yamlIdx, tomlIdx) { + if isLowerIndexThan(jsonIdx, yamlIdx, xmlIdx, tomlIdx) { return JSON } - if isLowerIndexThan(yamlIdx, tomlIdx) { + if isLowerIndexThan(yamlIdx, xmlIdx, tomlIdx) { return YAML } + if isLowerIndexThan(xmlIdx, tomlIdx) { + return XML + } + if tomlIdx != -1 { return TOML } diff --git a/parser/metadecoders/format_test.go b/parser/metadecoders/format_test.go index 2f625935e..0d94cf67e 100644 --- a/parser/metadecoders/format_test.go +++ b/parser/metadecoders/format_test.go @@ -30,6 +30,7 @@ func TestFormatFromString(t *testing.T) { {"json", JSON}, {"yaml", YAML}, {"yml", YAML}, + {"xml", XML}, {"toml", TOML}, {"config.toml", TOML}, {"tOMl", TOML}, @@ -48,6 +49,7 @@ func TestFormatFromMediaType(t *testing.T) { }{ {media.JSONType, JSON}, {media.YAMLType, YAML}, + {media.XMLType, XML}, {media.TOMLType, TOML}, {media.CalendarType, ""}, } { @@ -70,6 +72,7 @@ func TestFormatFromContentString(t *testing.T) { {`foo:"bar"`, YAML}, {`{ "foo": "bar"`, JSON}, {`a,b,c"`, CSV}, + {`<foo>bar</foo>"`, XML}, {`asdfasdf`, Format("")}, {``, Format("")}, } { |