}
switch finfo.flags & fMode {
- case fCharData:
+ case fCDATA, fCharData:
+ emit := EscapeText
+ if finfo.flags&fMode == fCDATA {
+ emit = emitCDATA
+ }
if err := s.trim(finfo.parents); err != nil {
return err
}
if err != nil {
return err
}
- Escape(p, data)
+ if err := emit(p, data); err != nil {
+ return err
+ }
continue
}
if vf.CanAddr() {
if err != nil {
return err
}
- Escape(p, data)
+ if err := emit(p, data); err != nil {
+ return err
+ }
continue
}
}
var scratch [64]byte
switch vf.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
+ if err := emit(p, strconv.AppendInt(scratch[:0], vf.Int(), 10)); err != nil {
+ return err
+ }
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
+ if err := emit(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10)); err != nil {
+ return err
+ }
case reflect.Float32, reflect.Float64:
- Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
+ if err := emit(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits())); err != nil {
+ return err
+ }
case reflect.Bool:
- Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
+ if err := emit(p, strconv.AppendBool(scratch[:0], vf.Bool())); err != nil {
+ return err
+ }
case reflect.String:
- if err := EscapeText(p, []byte(vf.String())); err != nil {
+ if err := emit(p, []byte(vf.String())); err != nil {
return err
}
case reflect.Slice:
if elem, ok := vf.Interface().([]byte); ok {
- if err := EscapeText(p, elem); err != nil {
+ if err := emit(p, elem); err != nil {
return err
}
}
Comment string `xml:",comment"`
}
+type CDataTest struct {
+ Chardata string `xml:",cdata"`
+}
+
+type NestedAndCData struct {
+ AB []string `xml:"A>B"`
+ CDATA string `xml:",cdata"`
+}
+
func ifaceptr(x interface{}) interface{} {
return &x
}
MyInt: 42,
},
},
+ // Test outputting CDATA-wrapped text.
+ {
+ ExpectXML: `<CDataTest></CDataTest>`,
+ Value: &CDataTest{},
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[http://example.com/tests/1?foo=1&bar=baz]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "http://example.com/tests/1?foo=1&bar=baz",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[Literal <![CDATA[Nested]]]]><![CDATA[>!]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "Literal <![CDATA[Nested]]>!",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "<![CDATA[Nested]]> Literal!",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal! <![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "<![CDATA[Nested]]> Literal! <![CDATA[Nested]]> Literal!",
+ },
+ },
+ {
+ ExpectXML: `<CDataTest><![CDATA[<![CDATA[<![CDATA[Nested]]]]><![CDATA[>]]]]><![CDATA[>]]></CDataTest>`,
+ Value: &CDataTest{
+ Chardata: "<![CDATA[<![CDATA[Nested]]>]]>",
+ },
+ },
+
// Test omitempty with parent chain; see golang.org/issue/4168.
{
ExpectXML: `<Strings><A></A></Strings>`,
ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"},
},
+ {
+ ExpectXML: `<NestedAndCData><A><B></B><B></B></A><![CDATA[test]]></NestedAndCData>`,
+ Value: &NestedAndCData{AB: make([]string, 2), CDATA: "test"},
+ },
}
func TestMarshal(t *testing.T) {
}
}
- case fCharData:
+ case fCDATA, fCharData:
if !saveData.IsValid() {
saveData = finfo.value(sv)
}
const (
fElement fieldFlags = 1 << iota
fAttr
+ fCDATA
fCharData
fInnerXml
fComment
fOmitEmpty
- fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
+ fMode = fElement | fAttr | fCDATA | fCharData | fInnerXml | fComment | fAny
)
var tinfoMap = make(map[reflect.Type]*typeInfo)
switch flag {
case "attr":
finfo.flags |= fAttr
+ case "cdata":
+ finfo.flags |= fCDATA
case "chardata":
finfo.flags |= fCharData
case "innerxml":
switch mode := finfo.flags & fMode; mode {
case 0:
finfo.flags |= fElement
- case fAttr, fCharData, fInnerXml, fComment, fAny:
+ case fAttr, fCDATA, fCharData, fInnerXml, fComment, fAny:
if f.Name == "XMLName" || tag != "" && mode != fAttr {
valid = false
}
EscapeText(w, s)
}
+var (
+ cdataStart = []byte("<![CDATA[")
+ cdataEnd = []byte("]]>")
+ cdataEscape = []byte("]]]]><![CDATA[>")
+)
+
+// emitCDATA writes to w the CDATA-wrapped plain text data s.
+// It escapes CDATA directives nested in s.
+func emitCDATA(w io.Writer, s []byte) error {
+ if len(s) == 0 {
+ return nil
+ }
+ if _, err := w.Write(cdataStart); err != nil {
+ return err
+ }
+ for {
+ i := bytes.Index(s, cdataEnd)
+ if i >= 0 && i+len(cdataEnd) <= len(s) {
+ // Found a nested CDATA directive end.
+ if _, err := w.Write(s[:i]); err != nil {
+ return err
+ }
+ if _, err := w.Write(cdataEscape); err != nil {
+ return err
+ }
+ i += len(cdataEnd)
+ } else {
+ if _, err := w.Write(s); err != nil {
+ return err
+ }
+ break
+ }
+ s = s[i:]
+ }
+ if _, err := w.Write(cdataEnd); err != nil {
+ return err
+ }
+ return nil
+}
+
// procInst parses the `param="..."` or `param='...'`
// value out of the provided string, returning "" if not found.
func procInst(param, s string) string {