Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,45 @@ options := xmlquery.ParserOptions{
doc, err := xmlquery.ParseWithOptions(utf8Reader, options)
```

### Parse non-ASCII characters and custom entity names.

UTF-8 XML can contain characters such as `ä` directly, or use a numeric
character reference such as `ä` or `ä`. These work with `Parse()`.
HTML entity names such as `ä` are not predefined in XML, so `Parse()`
reports an unknown entity. A character encoding conversion does not define
those entity names.

If the input uses known custom names, provide their replacements through
`DecoderOptions.Entity`:

```go
s := `<name>&auml; &amp; &#228;</name>`
options := xmlquery.ParserOptions{
Decoder: &xmlquery.DecoderOptions{
Strict: true,
Entity: map[string]string{"auml": "ä"},
},
}
doc, err := xmlquery.ParseWithOptions(strings.NewReader(s), options)
if err != nil {
panic(err)
}
fmt.Println(xmlquery.FindOne(doc, "//name").InnerText()) // ä & ä
```

Set `Strict: true` explicitly when supplying `DecoderOptions`: its zero value
is false, unlike the default XML decoder. This keeps malformed XML and unknown
entity names as errors. The entity map above defines only `auml`; it does not
load an HTML entity set or declarations from a DTD.

For XML bytes in another character encoding, such as ISO-8859-1, use
`DecoderOptions.CharsetReader`. `Parse()` already uses
`golang.org/x/net/html/charset.NewReaderLabel` for an XML encoding declaration.
Supplying `DecoderOptions` replaces these decoder defaults, so when combining
custom entities with a non-UTF-8 document, also set
`CharsetReader: charset.NewReaderLabel` (from `golang.org/x/net/html/charset`).
An entity map and a charset reader solve different problems.

### Query with custom namespace prefix.

```go
Expand Down
49 changes: 49 additions & 0 deletions doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/antchfx/xmlquery"
"github.com/antchfx/xpath"
"golang.org/x/net/html/charset"
)

func Example() {
Expand Down Expand Up @@ -62,3 +63,51 @@ func Example() {
prices := xpath.MustCompile(`sum(//book/price)`).Evaluate(nav).(float64)
fmt.Println(prices) // > Output: 56.85
}

func ExampleParseWithOptions_customEntities() {
s := `<name>&auml; &amp; &#228;</name>`
options := xmlquery.ParserOptions{
Decoder: &xmlquery.DecoderOptions{
Strict: true,
Entity: map[string]string{"auml": "ä"},
},
}
doc, err := xmlquery.ParseWithOptions(strings.NewReader(s), options)
if err != nil {
panic(err)
}
fmt.Println(xmlquery.FindOne(doc, "//name").InnerText())

for _, input := range []string{`<name>ä</name>`, `<name>&#228;</name>`, `<name>&#xE4;</name>`} {
doc, err := xmlquery.Parse(strings.NewReader(input))
if err != nil {
panic(err)
}
fmt.Println(xmlquery.FindOne(doc, "//name").InnerText())
}

_, err = xmlquery.Parse(strings.NewReader(`<name>&auml;</name>`))
fmt.Println(err != nil)
_, err = xmlquery.ParseWithOptions(strings.NewReader(`<name>&unknown;</name>`), options)
fmt.Println(err != nil)
_, err = xmlquery.ParseWithOptions(strings.NewReader(`<name></other>`), options)
fmt.Println(err != nil)

options.Decoder.CharsetReader = charset.NewReaderLabel
latin1 := "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><name>\xe4 &auml;</name>"
doc, err = xmlquery.ParseWithOptions(strings.NewReader(latin1), options)
if err != nil {
panic(err)
}
fmt.Println(xmlquery.FindOne(doc, "//name").InnerText())

// Output:
// ä & ä
// ä
// ä
// ä
// true
// true
// true
// ä ä
}