canonicaljson.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package cjson
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "reflect"
  8. "regexp"
  9. "sort"
  10. )
  11. /*
  12. encodeCanonicalString is a helper function to canonicalize the passed string
  13. according to the OLPC canonical JSON specification for strings (see
  14. http://wiki.laptop.org/go/Canonical_JSON). String canonicalization consists of
  15. escaping backslashes ("\") and double quotes (") and wrapping the resulting
  16. string in double quotes (").
  17. */
  18. func encodeCanonicalString(s string) string {
  19. re := regexp.MustCompile(`([\"\\])`)
  20. return fmt.Sprintf("\"%s\"", re.ReplaceAllString(s, "\\$1"))
  21. }
  22. /*
  23. encodeCanonical is a helper function to recursively canonicalize the passed
  24. object according to the OLPC canonical JSON specification (see
  25. http://wiki.laptop.org/go/Canonical_JSON) and write it to the passed
  26. *bytes.Buffer. If canonicalization fails it returns an error.
  27. */
  28. func encodeCanonical(obj interface{}, result *bytes.Buffer) (err error) {
  29. // Since this function is called recursively, we use panic if an error occurs
  30. // and recover in a deferred function, which is always called before
  31. // returning. There we set the error that is returned eventually.
  32. defer func() {
  33. if r := recover(); r != nil {
  34. err = errors.New(r.(string))
  35. }
  36. }()
  37. switch objAsserted := obj.(type) {
  38. case string:
  39. result.WriteString(encodeCanonicalString(objAsserted))
  40. case bool:
  41. if objAsserted {
  42. result.WriteString("true")
  43. } else {
  44. result.WriteString("false")
  45. }
  46. // The wrapping `EncodeCanonical` function decodes the passed json data with
  47. // `decoder.UseNumber` so that any numeric value is stored as `json.Number`
  48. // (instead of the default `float64`). This allows us to assert that it is a
  49. // non-floating point number, which are the only numbers allowed by the used
  50. // canonicalization specification.
  51. case json.Number:
  52. if _, err := objAsserted.Int64(); err != nil {
  53. panic(fmt.Sprintf("Can't canonicalize floating point number '%s'",
  54. objAsserted))
  55. }
  56. result.WriteString(objAsserted.String())
  57. case nil:
  58. result.WriteString("null")
  59. // Canonicalize slice
  60. case []interface{}:
  61. result.WriteString("[")
  62. for i, val := range objAsserted {
  63. if err := encodeCanonical(val, result); err != nil {
  64. return err
  65. }
  66. if i < (len(objAsserted) - 1) {
  67. result.WriteString(",")
  68. }
  69. }
  70. result.WriteString("]")
  71. case map[string]interface{}:
  72. result.WriteString("{")
  73. // Make a list of keys
  74. var mapKeys []string
  75. for key := range objAsserted {
  76. mapKeys = append(mapKeys, key)
  77. }
  78. // Sort keys
  79. sort.Strings(mapKeys)
  80. // Canonicalize map
  81. for i, key := range mapKeys {
  82. // Note: `key` must be a `string` (see `case map[string]interface{}`) and
  83. // canonicalization of strings cannot err out (see `case string`), thus
  84. // no error handling is needed here.
  85. encodeCanonical(key, result)
  86. result.WriteString(":")
  87. if err := encodeCanonical(objAsserted[key], result); err != nil {
  88. return err
  89. }
  90. if i < (len(mapKeys) - 1) {
  91. result.WriteString(",")
  92. }
  93. i++
  94. }
  95. result.WriteString("}")
  96. default:
  97. // We recover in a deferred function defined above
  98. panic(fmt.Sprintf("Can't canonicalize '%s' of type '%s'",
  99. objAsserted, reflect.TypeOf(objAsserted)))
  100. }
  101. return nil
  102. }
  103. /*
  104. EncodeCanonical JSON canonicalizes the passed object and returns it as a byte
  105. slice. It uses the OLPC canonical JSON specification (see
  106. http://wiki.laptop.org/go/Canonical_JSON). If canonicalization fails the byte
  107. slice is nil and the second return value contains the error.
  108. */
  109. func EncodeCanonical(obj interface{}) ([]byte, error) {
  110. // FIXME: Terrible hack to turn the passed struct into a map, converting
  111. // the struct's variable names to the json key names defined in the struct
  112. data, err := json.Marshal(obj)
  113. if err != nil {
  114. return nil, err
  115. }
  116. var jsonMap interface{}
  117. dec := json.NewDecoder(bytes.NewReader(data))
  118. dec.UseNumber()
  119. if err := dec.Decode(&jsonMap); err != nil {
  120. return nil, err
  121. }
  122. // Create a buffer and write the canonicalized JSON bytes to it
  123. var result bytes.Buffer
  124. if err := encodeCanonical(jsonMap, &result); err != nil {
  125. return nil, err
  126. }
  127. return result.Bytes(), nil
  128. }