github.com/tomwright/dasel@v1.27.3/storage/xml.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 8 "github.com/clbanning/mxj/v2" 9 "golang.org/x/net/html/charset" 10 ) 11 12 func init() { 13 // Required for https://github.com/TomWright/dasel/issues/61 14 mxj.XMLEscapeCharsDecoder(true) 15 16 // Required for https://github.com/TomWright/dasel/issues/164 17 mxj.XmlCharsetReader = charset.NewReaderLabel 18 19 registerReadParser([]string{"xml"}, []string{".xml"}, &XMLParser{}) 20 registerWriteParser([]string{"xml"}, []string{".xml"}, &XMLParser{}) 21 } 22 23 // XMLParser is a Parser implementation to handle xml files. 24 type XMLParser struct { 25 } 26 27 // FromBytes returns some data that is represented by the given bytes. 28 func (p *XMLParser) FromBytes(byteData []byte) (interface{}, error) { 29 if byteData == nil { 30 return nil, fmt.Errorf("cannot parse nil xml data") 31 } 32 if len(byteData) == 0 || strings.TrimSpace(string(byteData)) == "" { 33 return nil, nil 34 } 35 data, err := mxj.NewMapXml(byteData) 36 if err != nil { 37 return data, fmt.Errorf("could not unmarshal data: %w", err) 38 } 39 return &BasicSingleDocument{ 40 Value: map[string]interface{}(data), 41 }, nil 42 } 43 44 // ToBytes returns a slice of bytes that represents the given value. 45 func (p *XMLParser) ToBytes(value interface{}, options ...ReadWriteOption) ([]byte, error) { 46 buf := new(bytes.Buffer) 47 48 prettyPrint := true 49 colourise := false 50 indent := " " 51 52 for _, o := range options { 53 switch o.Key { 54 case OptionIndent: 55 if value, ok := o.Value.(string); ok { 56 indent = value 57 } 58 case OptionPrettyPrint: 59 if value, ok := o.Value.(bool); ok { 60 prettyPrint = value 61 } 62 case OptionColourise: 63 if value, ok := o.Value.(bool); ok { 64 colourise = value 65 } 66 } 67 } 68 69 writeMap := func(val interface{}) error { 70 if m, ok := val.(map[string]interface{}); ok { 71 mv := mxj.New() 72 for k, v := range m { 73 mv[k] = v 74 } 75 76 var byteData []byte 77 var err error 78 if prettyPrint { 79 byteData, err = mv.XmlIndent("", indent) 80 } else { 81 byteData, err = mv.Xml() 82 } 83 84 if err != nil { 85 return err 86 } 87 buf.Write(byteData) 88 buf.Write([]byte("\n")) 89 return nil 90 } 91 buf.Write([]byte(fmt.Sprintf("%v\n", val))) 92 return nil 93 } 94 95 switch d := value.(type) { 96 case SingleDocument: 97 if err := writeMap(d.Document()); err != nil { 98 return nil, err 99 } 100 case MultiDocument: 101 for _, dd := range d.Documents() { 102 if err := writeMap(dd); err != nil { 103 return nil, err 104 } 105 } 106 default: 107 if err := writeMap(d); err != nil { 108 return nil, err 109 } 110 } 111 112 if colourise { 113 if err := ColouriseBuffer(buf, "xml"); err != nil { 114 return nil, fmt.Errorf("could not colourise output: %w", err) 115 } 116 } 117 118 return buf.Bytes(), nil 119 }