decode.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package jsonschema
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. )
  7. // Schema holds a JSON schema.
  8. type Schema struct {
  9. // Name is the name of the property. For the parent/root property, this
  10. // is "root". For child properties, this is the name of the property.
  11. Name string `json:"-"`
  12. // Type is the type of the property.
  13. //
  14. // TODO: Union types (e.g. make this a []string).
  15. Type string
  16. // PrefixItems is a list of schemas for each item in a tuple. By
  17. // default, the tuple is "closed." unless Items is set to true or a
  18. // valid Schema.
  19. PrefixItems []*Schema
  20. // Items is the schema for each item in a list.
  21. //
  22. // If it is missing, or its JSON value is "null" or "false", it is nil.
  23. // If the JSON value is "true", it is set to the empty Schema. If the
  24. // JSON value is an object, it will be decoded as a Schema.
  25. Items *Schema
  26. // MinItems specifies the minimum number of items allowed in a list.
  27. MinItems int
  28. // MaxItems specifies the maximum number of items allowed in a list.
  29. MaxItems int
  30. // Properties is the schema for each property of an object.
  31. Properties []*Schema
  32. // Format is the format of the property. This is used to validate the
  33. // property against a specific format.
  34. //
  35. // It is the callers responsibility to validate the property against
  36. // the format.
  37. Format string
  38. // Minimum specifies the minimum value for numeric properties.
  39. Minimum float64
  40. // Maximum specifies the maximum value for numeric properties.
  41. Maximum float64
  42. // Enum is a list of valid values for the property.
  43. Enum []json.RawMessage
  44. }
  45. func (s *Schema) UnmarshalJSON(data []byte) error {
  46. type S Schema
  47. w := struct {
  48. Properties props
  49. Items items
  50. *S
  51. }{
  52. S: (*S)(s),
  53. }
  54. if err := json.Unmarshal(data, &w); err != nil {
  55. return err
  56. }
  57. if w.Items.set {
  58. s.Items = &w.Items.Schema
  59. }
  60. s.Properties = w.Properties
  61. return nil
  62. }
  63. type items struct {
  64. Schema
  65. set bool
  66. }
  67. func (s *items) UnmarshalJSON(data []byte) error {
  68. switch b := data[0]; b {
  69. case 't':
  70. *s = items{set: true}
  71. case '{':
  72. type I items
  73. if err := json.Unmarshal(data, (*I)(s)); err != nil {
  74. return err
  75. }
  76. s.set = true
  77. case 'n', 'f':
  78. default:
  79. return errors.New("invalid Items")
  80. }
  81. return nil
  82. }
  83. // EffectiveType returns the effective type of the schema. If the Type field is
  84. // not empty, it is returned; otherwise:
  85. //
  86. // - If the schema has both Properties and Items, it returns an empty string.
  87. // - If the schema has Properties, it returns "object".
  88. // - If the schema has Items, it returns "array".
  89. // - If the schema has neither Properties nor Items, it returns "value".
  90. //
  91. // The returned string is never empty.
  92. func (d *Schema) EffectiveType() string {
  93. if d.Type == "" {
  94. if len(d.Properties) > 0 {
  95. return "object"
  96. }
  97. if len(d.PrefixItems) > 0 || d.Items != nil {
  98. return "array"
  99. }
  100. return "value"
  101. }
  102. return d.Type
  103. }
  104. // props is an ordered list of properties. The order of the properties
  105. // is the order in which they were defined in the schema.
  106. type props []*Schema
  107. var _ json.Unmarshaler = (*props)(nil)
  108. func (v *props) UnmarshalJSON(data []byte) error {
  109. if len(data) == 0 {
  110. return nil
  111. }
  112. if data[0] != '{' {
  113. return errors.New("expected object")
  114. }
  115. d := json.NewDecoder(bytes.NewReader(data))
  116. // TODO(bmizerany): Consider DisallowUnknownFields. Currently, we, like
  117. // llama.cpp, ignore unknown fields, which could be lead to unexpected
  118. // behavior for clients of this package, since they may not be aware
  119. // that "additionalFields", "itemsPrefix", etc, are being ignored.
  120. //
  121. // For now, just do what llama.cpp does.
  122. t, err := d.Token()
  123. if err != nil {
  124. return err
  125. }
  126. if t != json.Delim('{') {
  127. return errors.New("expected object")
  128. }
  129. for d.More() {
  130. // Use the first token (map key) as the property name, then
  131. // decode the rest of the object fields into a Schema and
  132. // append.
  133. t, err := d.Token()
  134. if err != nil {
  135. return err
  136. }
  137. if t == json.Delim('}') {
  138. return nil
  139. }
  140. s := &Schema{
  141. Name: t.(string),
  142. }
  143. if err := d.Decode(s); err != nil {
  144. return err
  145. }
  146. *v = append(*v, s)
  147. }
  148. return nil
  149. }