summaryrefslogtreecommitdiffstats
path: root/parser
diff options
context:
space:
mode:
authorPaul van Brouwershaven <vanbroup@users.noreply.github.com>2021-12-02 17:30:36 +0100
committerGitHub <noreply@github.com>2021-12-02 17:30:36 +0100
commit0eaaa8fee37068bfc8ecfb760f770ecc9a7af22a (patch)
tree95cf7c5ac3a7e56c0eb411a28cae5c0412a510bd /parser
parent58adbeef88ea5c8769d12ba27eef2d89bdf575eb (diff)
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.go9
-rw-r--r--parser/metadecoders/decoder.go20
-rw-r--r--parser/metadecoders/decoder_test.go55
-rw-r--r--parser/metadecoders/format.go16
-rw-r--r--parser/metadecoders/format_test.go3
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("")},
} {