encode.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package plist
  2. import (
  3. "bytes"
  4. "errors"
  5. "io"
  6. "reflect"
  7. "runtime"
  8. )
  9. type generator interface {
  10. generateDocument(cfValue)
  11. Indent(string)
  12. }
  13. // An Encoder writes a property list to an output stream.
  14. type Encoder struct {
  15. writer io.Writer
  16. format int
  17. indent string
  18. }
  19. // Encode writes the property list encoding of v to the stream.
  20. func (p *Encoder) Encode(v interface{}) (err error) {
  21. defer func() {
  22. if r := recover(); r != nil {
  23. if _, ok := r.(runtime.Error); ok {
  24. panic(r)
  25. }
  26. err = r.(error)
  27. }
  28. }()
  29. pval := p.marshal(reflect.ValueOf(v))
  30. if pval == nil {
  31. panic(errors.New("plist: no root element to encode"))
  32. }
  33. var g generator
  34. switch p.format {
  35. case XMLFormat:
  36. g = newXMLPlistGenerator(p.writer)
  37. case BinaryFormat, AutomaticFormat:
  38. g = newBplistGenerator(p.writer)
  39. case OpenStepFormat, GNUStepFormat:
  40. g = newTextPlistGenerator(p.writer, p.format)
  41. }
  42. g.Indent(p.indent)
  43. g.generateDocument(pval)
  44. return
  45. }
  46. // Indent turns on pretty-printing for the XML and Text property list formats.
  47. // Each element begins on a new line and is preceded by one or more copies of indent according to its nesting depth.
  48. func (p *Encoder) Indent(indent string) {
  49. p.indent = indent
  50. }
  51. // NewEncoder returns an Encoder that writes an XML property list to w.
  52. func NewEncoder(w io.Writer) *Encoder {
  53. return NewEncoderForFormat(w, XMLFormat)
  54. }
  55. // NewEncoderForFormat returns an Encoder that writes a property list to w in the specified format.
  56. // Pass AutomaticFormat to allow the library to choose the best encoding (currently BinaryFormat).
  57. func NewEncoderForFormat(w io.Writer, format int) *Encoder {
  58. return &Encoder{
  59. writer: w,
  60. format: format,
  61. }
  62. }
  63. // NewBinaryEncoder returns an Encoder that writes a binary property list to w.
  64. func NewBinaryEncoder(w io.Writer) *Encoder {
  65. return NewEncoderForFormat(w, BinaryFormat)
  66. }
  67. // Marshal returns the property list encoding of v in the specified format.
  68. //
  69. // Pass AutomaticFormat to allow the library to choose the best encoding (currently BinaryFormat).
  70. //
  71. // Marshal traverses the value v recursively.
  72. // Any nil values encountered, other than the root, will be silently discarded as
  73. // the property list format bears no representation for nil values.
  74. //
  75. // Strings, integers of varying size, floats and booleans are encoded unchanged.
  76. // Strings bearing non-ASCII runes will be encoded differently depending upon the property list format:
  77. // UTF-8 for XML property lists and UTF-16 for binary property lists.
  78. //
  79. // Slice and Array values are encoded as property list arrays, except for
  80. // []byte values, which are encoded as data.
  81. //
  82. // Map values encode as dictionaries. The map's key type must be string; there is no provision for encoding non-string dictionary keys.
  83. //
  84. // Struct values are encoded as dictionaries, with only exported fields being serialized. Struct field encoding may be influenced with the use of tags.
  85. // The tag format is:
  86. //
  87. // `plist:"<key>[,flags...]"`
  88. //
  89. // The following flags are supported:
  90. //
  91. // omitempty Only include the field if it is not set to the zero value for its type.
  92. //
  93. // If the key is "-", the field is ignored.
  94. //
  95. // Anonymous struct fields are encoded as if their exported fields were exposed via the outer struct.
  96. //
  97. // Pointer values encode as the value pointed to.
  98. //
  99. // Channel, complex and function values cannot be encoded. Any attempt to do so causes Marshal to return an error.
  100. func Marshal(v interface{}, format int) ([]byte, error) {
  101. return MarshalIndent(v, format, "")
  102. }
  103. // MarshalIndent works like Marshal, but each property list element
  104. // begins on a new line and is preceded by one or more copies of indent according to its nesting depth.
  105. func MarshalIndent(v interface{}, format int, indent string) ([]byte, error) {
  106. buf := &bytes.Buffer{}
  107. enc := NewEncoderForFormat(buf, format)
  108. enc.Indent(indent)
  109. if err := enc.Encode(v); err != nil {
  110. return nil, err
  111. }
  112. return buf.Bytes(), nil
  113. }