How to encode/decode a string to gson - json

I need to decode and encode strings using the gson format (it's a json dialect). Below is the code I came up with so far translated from the gson(Java) library
For some reasons none of the characters I try to decode/replace is actually replaced. I believe I'm doing something wrong with the character escaping(I'm new to go) so any direction/help to fix it would be appreciated.
Go Playground
package main
import (
"bytes"
"fmt"
"strings"
)
const s = `https:\/\/exampple.com\/static\/_\/js\/k\x3dgaia.gaiafe_glif.en.nnMHsIffkD4.O\/m\x3dglifb,identifier,unknownerror\/am\x3dggIgAAAAAAEKBCEImA2CYiCxoQo\/rt\x3dj\/d\x3d1\/rs\x3dABkqax3Fc8CWFtgWOYXlvHJI_bE3oVSwgA`
const des = `https://exampple.com/static/_/js/k=gaia.gaiafe_glif.en.nnMHsIffkD4.O/m=P9M9H,HUb4Ab,sy1m,sy1n,emo,sy1k,sy54,gsfs7c/am=ggIgAAAAAAEKBCEImA2CYiCxoQo/rt=j/d=0/rs=ABkqax3Fc8CWFtgWOYXlvHJI_bE3oVSwgA`
func main() {
dec, err := Decode(s, true)
if err != nil {
panic(err)
}
if dec != des {
fmt.Printf("expected \n %v \ngot \n %s", dec, des)
}
}
type replacementChar map[rune]string
func (r replacementChar) get(k rune) string {
return fmt.Sprintf("\\u%04x", r[k])
}
var replacement = replacementChar{
'"': "\\\"",
'\\': "\\\\",
'\t': "\\t",
'\b': "\\b",
'\n': "\\n",
'\r': "\\r",
'\f': "\\f",
}
var htmlReplacement = replacementChar{
'<': "\\u003c",
'>': "\\u003e",
'&': "\\u0026",
'=': "\\u003d",
'\'': "\\u0027",
}
var extraReplacement = replacementChar{
'\u2028': "\\u2028",
'\u2029': "\\u2029",
}
/*
* From RFC 7159, "All Unicode characters may be placed within the
* quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
*
* We also escape '\u2028' and '\u2029', which JavaScript interprets as
* newline characters. This prevents eval() from failing with a syntax
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/
func Encode(s string, htmlSafe bool) (string, error) {
buf := bytes.NewBuffer([]byte("\""))
var err error
for _, r := range s {
switch {
case replacement[r] != "":
_, err = buf.WriteString(replacement.get(r))
if err != nil {
return "", err
}
case htmlSafe && htmlReplacement[r] != "":
_, err = buf.WriteString(htmlReplacement.get(r))
if err != nil {
return "", err
}
case extraReplacement[r] != "":
_, err = buf.WriteString(extraReplacement.get(r))
if err != nil {
return "", err
}
default:
_, err = buf.WriteRune(r)
if err != nil {
return "", err
}
}
}
buf.WriteString("\"")
return buf.String(), nil
}
var decodeHTMLReplacementPair []string
var decodeReplacementPair []string
var decodeReplacementAll []string
func init() {
for k, _ := range htmlReplacement {
decodeHTMLReplacementPair = append(decodeHTMLReplacementPair, htmlReplacement.get(k))
decodeHTMLReplacementPair = append(decodeHTMLReplacementPair, string(k))
}
for k, _ := range replacement {
decodeReplacementPair = append(decodeReplacementPair, replacement.get(k))
decodeReplacementPair = append(decodeReplacementPair, string(k))
}
for k, _ := range extraReplacement {
decodeReplacementPair = append(decodeReplacementPair, extraReplacement.get(k))
decodeReplacementPair = append(decodeReplacementPair, string(k))
decodeHTMLReplacementPair = append(decodeHTMLReplacementPair, extraReplacement.get(k))
decodeHTMLReplacementPair = append(decodeHTMLReplacementPair, string(k))
}
decodeReplacementAll = append(decodeHTMLReplacementPair, decodeReplacementPair...)
}
func Decode(s string, htmlSafe bool) (string, error) {
var r *strings.Replacer
if !htmlSafe {
r = strings.NewReplacer(decodeHTMLReplacementPair...)
} else {
r = strings.NewReplacer(decodeReplacementAll...)
}
return r.Replace(s), nil
}
/* Original Java Source https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/stream/JsonWriter.java
private static final String[] REPLACEMENT_CHARS;
private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
static {
REPLACEMENT_CHARS = new String[128];
for (int i = 0; i <= 0x1f; i++) {
REPLACEMENT_CHARS[i] = String.format("\\u%04x", (int) i);
}
REPLACEMENT_CHARS['"'] = "\\\"";
REPLACEMENT_CHARS['\\'] = "\\\\";
REPLACEMENT_CHARS['\t'] = "\\t";
REPLACEMENT_CHARS['\b'] = "\\b";
REPLACEMENT_CHARS['\n'] = "\\n";
REPLACEMENT_CHARS['\r'] = "\\r";
REPLACEMENT_CHARS['\f'] = "\\f";
HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
}
private void string(String value) throws IOException {
String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
out.write("\"");
int last = 0;
int length = value.length();
for (int i = 0; i < length; i++) {
char c = value.charAt(i);
String replacement;
if (c < 128) {
replacement = replacements[c];
if (replacement == null) {
continue;
}
} else if (c == '\u2028') {
replacement = "\\u2028";
} else if (c == '\u2029') {
replacement = "\\u2029";
} else {
continue;
}
if (last < i) {
out.write(value, last, i - last);
}
out.write(replacement);
last = i + 1;
}
if (last < length) {
out.write(value, last, length - last);
}
out.write("\"");
}
*/

You are currently not replacing all kinds of escape combinations that would start with \, such as \/.
const s and des are not the same strings, no matter how you decode it
s does not contain a valid JSON encoded string as \x sequences are not valid.
Anyhow, since gson uses JSON (RFC 7159), I suggest using an existing package for this work. Try encoding/json:
func main() {
var dec string
err := json.Unmarshal([]byte(`"`+s+`"`), &dec)
if err != nil {
panic(err)
}
if dec != des {
fmt.Printf("expected \n %v \ngot \n %s", dec, des)
}
}
PS. The downvotes are most likely because your question can easily be considered code review, being more suitable for https://codereview.stackexchange.com

Related

How to write custom splitFunc for bufio.Scaner that scan json objects

I have a code like this
scanner := bufio.NewScanner(reader)
scanner.Split(splitJSON)
for scanner.Scan() {
bb := scanner.Bytes()
}
I would like to get from Scanner only valid JSON objects one at a time. In some case in Scanner may be bytes that represent struct like this
{
"some_object": "name",
"some_fileds": {}
}
{
"some_object":
}
I need only the first part of this
{
"some_object": "name",
"some_fileds": {}
}
For the other, I should wait for the end of JSON object.
I have a function like this, but it's horrible and doesn't work.
func splitJSON(
bb []byte, atEOF bool,
) (advance int, token []byte, err error) {
print(string(bb))
if len(bb) < 10 {
return 0, nil, nil
}
var nested, from, to int
var end bool
for i, b := range bb {
if string(b) == "{" {
if end {
to = i
break
}
if nested == 0 {
from = i
}
nested++
}
if string(b) == "}" {
nested--
if nested == 0 {
to = i
end = true
}
}
}
if atEOF {
return len(bb), bb, nil
}
return len(bb[from:to]), bb[from:to], nil
}
UPD
It was decided by this splitFunc
func splitJSON(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
reader := bytes.NewReader(data)
dec := json.NewDecoder(reader)
var raw json.RawMessage
if err := dec.Decode(&raw); err != nil {
return 0, nil, nil
}
return len(raw) + 1, raw, nil
}
Use json.Decoder for this. Each Decoder.Decode() call will decode the next JSON-encoded value from the input, JSON objects in your case.
If you don't want to decode the JSON objects just need the JSON data (byte slice), use a json.RawMessage to unmarshal into.
For example:
func main() {
reader := strings.NewReader(src)
dec := json.NewDecoder(reader)
for {
var raw json.RawMessage
if err := dec.Decode(&raw); err != nil {
if err == io.EOF {
break
}
fmt.Printf("Error:", err)
return
}
fmt.Println("Next:", string(raw))
}
}
const src = `{
"some_object": "name",
"some_fileds": {}
}
{
"some_object": "foo"
}`
This will output (try it on the Go Playground):
Next: {
"some_object": "name",
"some_fileds": {}
}
Next: {
"some_object": "foo"
}

Why is my function failing to trim whitespace on one URL, but working fine on another?

I have a Dataset Struct, which looks like this -
type Dataset struct {
Publications []GeneralDetails `bson:"publications,omitempty" json:"publications,omitempty"`
URI string `bson:"uri,omitempty" json:"uri,omitempty"`
}
And a GeneralDetails struct, which looks like this -
type GeneralDetails struct {
Description string `bson:"description,omitempty" json:"description,omitempty"`
HRef string `bson:"href,omitempty" json:"href,omitempty"`
Title string `bson:"title,omitempty" json:"title,omitempty"`
}
I have a ValidateDastaset funciton which trims whitespace and returns a parsed URL -
func ValidateDataset(ctx context.Context, dataset *Dataset) error {
var generalDetails = &GeneralDetails{}
var invalidFields []string
if dataset.URI != "" {
dataset.URI = strings.TrimSpace(dataset.URI)
_, err := url.Parse(dataset.URI)
if err != nil {
invalidFields = append(invalidFields, "URI")
log.Event(ctx, "error parsing URI", log.ERROR, log.Error(err))
}
}
if dataset.Publications != nil {
generalDetails.HRef = strings.TrimSpace(generalDetails.HRef)
_, err := url.Parse(generalDetails.HRef)
if err != nil {
invalidFields = append(invalidFields, "href")
log.Event(ctx, "error parsing URI", log.ERROR, log.Error(err))
}
}
if invalidFields != nil {
return fmt.Errorf("invalid fields: %v", invalidFields)
}
return nil
}
However, the whitespace is only being trimmed on dataset.URI, but not on generalDetails.HRef and I really don't understand why. Is anyone able to help please?
Edit: here's what's in my test package, which is how I know the whitespace isn't being trimmed -
var testPublications = GeneralDetails{
Description: "some publication description",
HRef: "http://localhost:22000//datasets/publications",
Title: "some publication title",
}
func createDataset() Dataset {
return Dataset{
ID: "123",
URI: "http://localhost:22000/datasets/123",
Publications: []GeneralDetails{
{Description: "some publication description"},
{HRef: "http://localhost:22000//datasets/publications"},
{Title: "some publication title"},
},
}
}
}
func createGeneralDetails() GeneralDetails {
return testPublications
}
And the actual test itself, which is failing -
Convey("Successful validation (true) returned", t, func() {
Convey("when generalDetails.Href contains whitespace it should not return an error ", func() {
dataset := createDataset()
dataset.ID = "123"
generalDetails := createGeneralDetails()
generalDetails.HRef = " http://localhost:22000//datasets/publications "
validationErr := ValidateDataset(testContext, &dataset)
So(validationErr, ShouldBeNil)
So(generalDetails.HRef, ShouldEqual, "http://localhost:22000//datasets/publications")
})
})
In the function ValidateDataset, you created a
var generalDetails = &GeneralDetails{}
But you never assign to this variable, or iterate the Publications field of the passed in *Dataset
if dataset.Publications != nil {
generalDetails.HRef = strings.TrimSpace(generalDetails.HRef)
_, err := url.Parse(generalDetails.HRef)
if err != nil {
invalidFields = append(invalidFields, "href")
log.Event(ctx, "error parsing URI", log.ERROR, log.Error(err))
}
}
You're just editing the nil generalDetails var, and forgetting completely about dataset.Publications.
You'd need to do something like
for i := range dataset.Publications {
generalDetails = &dataset.Publications[i]
// validate
}

How to replace/update a key value inside a json array in golang?

I have a json array where it contains some flags as key and I have set the default values for those keys as false. this is my json array.
var flags = map[string]bool{
"terminationFlag": false,
"transferFlag": false,
"jrCancelledFlag": false,
"jrFilledFlag": false,
}
On performing an operation in a for loop, i have to update 1 field in the above json array as true. During the next iteration, it has to update the 2nd field in the json array as true. After all the fields in the json array is set to true, I have to return the json array.
the code i tried:
Keystrings := []string{"terminationReport - 2019-1","transferReport - 2019-1","jrCancelledReport - 2019-1","jrFilledReport - 2019-1"}
fmt.Println("Keystrings ", Keystrings)
for i,value := range Keystrings {
bytesread, err = stub.GetState(value)
var result []string
_ = json.Unmarshal(bytesread, &result)
fmt.Println("result ", result)
if result[0] == "yes"{
fmt.Println("result in if ", result)
flags[i] = true
}
}
Since it's very hard to understand from the question what is being asked, here's a simple attempt at working with similar data as the question, in the hope that you can take the right parts from this sample and adapt them to your issue. Follow the comments in the code to understand what's going on.
package main
import (
"encoding/json"
"fmt"
"log"
)
var jsonBlob = []byte(`["jrCancelledFlag", "yes"]`)
var flags = map[string]bool{
"terminationFlag": false,
"transferFlag": false,
"jrCancelledFlag": false,
"jrFilledFlag": false,
}
func main() {
// Parse jsonBlob into a slice of strings
var parsed []string
if err := json.Unmarshal(jsonBlob, &parsed); err != nil {
log.Fatalf("JSON unmarshal: %s", err)
}
// Expect the slice to be of length 2, first item flag name, second item
// yes/no.
if len(parsed) != 2 {
log.Fatalf("parsed len %d, expected 2", len(parsed))
}
// Assume parsed[0] actually appears in flags... otherwise more error checking
// is needed.
if parsed[1] == "yes" {
flags[parsed[0]] = true
}
// Emit updated flags as json
json, err := json.Marshal(flags)
if err != nil {
log.Fatalf("JSON marshal: %s", err)
}
fmt.Println(string(json))
}
This can be achieved cleaning by using the JSON interface to define your own unmarshaller
https://medium.com/#nate510/dynamic-json-umarshalling-in-go-88095561d6a0
package main
import (
"encoding/json"
"fmt"
"log"
)
var jsonBlob = []byte(`["jrCancelledFlag", "yes"]`)
// Flags ...
type Flags struct {
TerminationFlag bool `json:"terminationFlag,omitempty"`
TransferFlag bool `json:"transferFlag,omitempty"`
JRCancelledFlag bool `json:"jrCancelledFlag,omitempty"`
JRFilledFlag bool `json:"jrFilledFlag,omitempty"`
}
// UnmarshalJSON satisfies the JSON unmarshaller interface
func (f *Flags) UnmarshalJSON(data []byte) error {
var parsed []string
if err := json.Unmarshal(jsonBlob, &parsed); err != nil {
return err
}
if len(parsed)%2 != 0 {
return fmt.Errorf("expected string to be evenly paired")
}
for i := 0; i < len(parsed); i++ {
j := i + 1
if j < len(parsed) {
switch parsed[i] {
case "terminationFlag":
f.TerminationFlag = toBool(parsed[j])
case "transferFlag":
f.TransferFlag = toBool(parsed[j])
case "jrCancelledFlag":
f.JRCancelledFlag = toBool(parsed[j])
case "jrFilledFlag":
f.JRFilledFlag = toBool(parsed[j])
}
}
}
return nil
}
func toBool(s string) bool {
if s == "yes" {
return true
}
return false
}
func main() {
var flags Flags
err := json.Unmarshal(jsonBlob, &flags)
if err != nil {
log.Fatal(err)
}
b, _ := json.Marshal(flags)
fmt.Println(string(b))
}

Appending to json file without writing entire file

I have a json which contains one its attributes value as an array and I need to keep appending values to the array and write to a file. Is there a way I could avoid rewrite of the existing data and only append the new values?
----- Moving next question on different thread ---------------
what is recommended way for writing big data sets onto the file incremental file write or file dump at the end process?
A general solution makes the most sense if the existing JSON is actually an array, or if it's an object that has an array as the last or only pair, as in your case. Otherwise, you're inserting instead of appending. You probably don't want to read the entire file either.
One approach is not much different than what you were thinking, but handles several details
Read the end of the file to verify that it "ends with an array"
Retain that part
Position the file at that ending array bracket
Take the output from a standard encoder for an array of new data, dropping its opening bracket, and inserting a comma if necessary
The end of the the new output replaces the original ending array bracket
Tack the rest of the tail back on
import (
"bytes"
"errors"
"io"
"io/ioutil"
"os"
"regexp"
"unicode"
)
const (
tailCheckLen = 16
)
var (
arrayEndsObject = regexp.MustCompile("(\\[\\s*)?](\\s*}\\s*)$")
justArray = regexp.MustCompile("(\\[\\s*)?](\\s*)$")
)
type jsonAppender struct {
f *os.File
strippedBracket bool
needsComma bool
tail []byte
}
func (a jsonAppender) Write(b []byte) (int, error) {
trimmed := 0
if !a.strippedBracket {
t := bytes.TrimLeftFunc(b, unicode.IsSpace)
if len(t) == 0 {
return len(b), nil
}
if t[0] != '[' {
return 0, errors.New("not appending array: " + string(t))
}
trimmed = len(b) - len(t) + 1
b = t[1:]
a.strippedBracket = true
}
if a.needsComma {
a.needsComma = false
n, err := a.f.Write([]byte(", "))
if err != nil {
return n, err
}
}
n, err := a.f.Write(b)
return trimmed + n, err
}
func (a jsonAppender) Close() error {
if _, err := a.f.Write(a.tail); err != nil {
defer a.f.Close()
return err
}
return a.f.Close()
}
func JSONArrayAppender(file string) (io.WriteCloser, error) {
f, err := os.OpenFile(file, os.O_RDWR, 0664)
if err != nil {
return nil, err
}
pos, err := f.Seek(0, io.SeekEnd)
if err != nil {
return nil, err
}
if pos < tailCheckLen {
pos = 0
} else {
pos -= tailCheckLen
}
_, err = f.Seek(pos, io.SeekStart)
if err != nil {
return nil, err
}
tail, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
hasElements := false
if len(tail) == 0 {
_, err = f.Write([]byte("["))
if err != nil {
return nil, err
}
} else {
var g [][]byte
if g = arrayEndsObject.FindSubmatch(tail); g != nil {
} else if g = justArray.FindSubmatch(tail); g != nil {
} else {
return nil, errors.New("does not end with array")
}
hasElements = len(g[1]) == 0
_, err = f.Seek(-int64(len(g[2])+1), io.SeekEnd) // 1 for ]
if err != nil {
return nil, err
}
tail = g[2]
}
return jsonAppender{f: f, needsComma: hasElements, tail: tail}, nil
}
Usage is then like in this test fragment
a, err := JSONArrayAppender(f)
if err != nil {
t.Fatal(err)
}
added := []struct {
Name string `json:"name"`
}{
{"Wonder Woman"},
}
if err = json.NewEncoder(a).Encode(added); err != nil {
t.Fatal(err)
}
if err = a.Close(); err != nil {
t.Fatal(err)
}
You can use whatever settings on the Encoder you want. The only hard-coded part is handling needsComma, but you can add an argument for that.
If your JSON array is simple you can use something like the following code. In this code, I create JSON array manually.
type item struct {
Name string
}
func main() {
fd, err := os.Create("hello.json")
if err != nil {
log.Fatal(err)
}
fd.Write([]byte{'['})
for i := 0; i < 10; i++ {
b, err := json.Marshal(item{
"parham",
})
if err != nil {
log.Fatal(err)
}
if i != 0 {
fd.Write([]byte{','})
}
fd.Write(b)
}
fd.Write([]byte{']'})
}
If you want to have a valid array in each step you can write ']' at the end of each iteration and then seek back on the start of the next iteration.

Extracting positional offset of *html.Node in Golang

How do I can extract positional offset for specific node of already parsed HTML document? For example, for document <div>Hello, <b>World!</b></div> I want to be able to know that offset of World! is 15:21. Document may be changed while parsing.
I have a solution to render whole document with special marks, but it's really bad for performance. Any ideas?
package main
import (
"bytes"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
"log"
"strings"
)
func nodeIndexOffset(context *html.Node, node *html.Node) (int, int) {
if node.Type != html.TextNode {
node = node.FirstChild
}
originalData := node.Data
var buf bytes.Buffer
node.Data = "|start|" + originalData
_ = html.Render(&buf, context.FirstChild)
start := strings.Index(buf.String(), "|start|")
buf = bytes.Buffer{}
node.Data = originalData + "|end|"
_ = html.Render(&buf, context.FirstChild)
end := strings.Index(buf.String(), "|end|")
node.Data = originalData
return start, end
}
func main() {
s := "<div>Hello, <b>World!</b></div>"
var context html.Node
context = html.Node{
Type: html.ElementNode,
Data: "body",
DataAtom: atom.Body,
}
nodes, err := html.ParseFragment(strings.NewReader(s), &context)
if err != nil {
log.Fatal(err)
}
for _, node := range nodes {
context.AppendChild(node)
}
world := nodes[0].FirstChild.NextSibling.FirstChild
log.Println("target", world)
log.Println(nodeIndexOffset(&context, world))
}
Not an answer, but too long for a comment. The following could work to some extent:
Use a Tokenizer and step through each element one by one.
Wrap your input into a custom reader which records lines and
column offsets as the Tokenizer reads from it.
Query your custom reader for the position before and after calling Next()
to record the approximate position information you need.
This is a bit painful and not too accurate but probably the best you could do.
I come up with solution where we extend (please fix me if there's another way to do it) original HTML package with additional custom.go file with new exported function. This function is able to access unexported data property of Tokenizer, which holds exactly start and end position of current Node. We have to adjust positions after each buffer read. See globalBufDif.
I don't really like that I have to fork the package only to access couple of properties, but seems like this is a Go way.
func parseWithIndexes(p *parser) (map[*Node][2]int, error) {
// Iterate until EOF. Any other error will cause an early return.
var err error
var globalBufDif int
var prevEndBuf int
var tokenIndex [2]int
tokenMap := make(map[*Node][2]int)
for err != io.EOF {
// CDATA sections are allowed only in foreign content.
n := p.oe.top()
p.tokenizer.AllowCDATA(n != nil && n.Namespace != "")
t := p.top().FirstChild
for {
if t != nil && t.NextSibling != nil {
t = t.NextSibling
} else {
break
}
}
tokenMap[t] = tokenIndex
if prevEndBuf > p.tokenizer.data.end {
globalBufDif += prevEndBuf
}
prevEndBuf = p.tokenizer.data.end
// Read and parse the next token.
p.tokenizer.Next()
tokenIndex = [2]int{p.tokenizer.data.start + globalBufDif, p.tokenizer.data.end + globalBufDif}
p.tok = p.tokenizer.Token()
if p.tok.Type == ErrorToken {
err = p.tokenizer.Err()
if err != nil && err != io.EOF {
return tokenMap, err
}
}
p.parseCurrentToken()
}
return tokenMap, nil
}
// ParseFragmentWithIndexes parses a fragment of HTML and returns the nodes
// that were found. If the fragment is the InnerHTML for an existing element,
// pass that element in context.
func ParseFragmentWithIndexes(r io.Reader, context *Node) ([]*Node, map[*Node][2]int, error) {
contextTag := ""
if context != nil {
if context.Type != ElementNode {
return nil, nil, errors.New("html: ParseFragment of non-element Node")
}
// The next check isn't just context.DataAtom.String() == context.Data because
// it is valid to pass an element whose tag isn't a known atom. For example,
// DataAtom == 0 and Data = "tagfromthefuture" is perfectly consistent.
if context.DataAtom != a.Lookup([]byte(context.Data)) {
return nil, nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data)
}
contextTag = context.DataAtom.String()
}
p := &parser{
tokenizer: NewTokenizerFragment(r, contextTag),
doc: &Node{
Type: DocumentNode,
},
scripting: true,
fragment: true,
context: context,
}
root := &Node{
Type: ElementNode,
DataAtom: a.Html,
Data: a.Html.String(),
}
p.doc.AppendChild(root)
p.oe = nodeStack{root}
p.resetInsertionMode()
for n := context; n != nil; n = n.Parent {
if n.Type == ElementNode && n.DataAtom == a.Form {
p.form = n
break
}
}
tokenMap, err := parseWithIndexes(p)
if err != nil {
return nil, nil, err
}
parent := p.doc
if context != nil {
parent = root
}
var result []*Node
for c := parent.FirstChild; c != nil; {
next := c.NextSibling
parent.RemoveChild(c)
result = append(result, c)
c = next
}
return result, tokenMap, nil
}