Message+JSONArrayAdditions.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Sources/SwiftProtobuf/Array+JSONAdditions.swift - JSON format primitive types
  2. //
  3. // Copyright (c) 2014 - 2017 Apple Inc. and the project authors
  4. // Licensed under Apache License v2.0 with Runtime Library Exception
  5. //
  6. // See LICENSE.txt for license information:
  7. // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
  8. //
  9. // -----------------------------------------------------------------------------
  10. ///
  11. /// Extensions to `Array` to support JSON encoding/decoding.
  12. ///
  13. // -----------------------------------------------------------------------------
  14. import Foundation
  15. /// JSON encoding and decoding methods for arrays of messages.
  16. extension Message {
  17. /// Returns a string containing the JSON serialization of the messages.
  18. ///
  19. /// Unlike binary encoding, presence of required fields is not enforced when
  20. /// serializing to JSON.
  21. ///
  22. /// - Returns: A string containing the JSON serialization of the messages.
  23. /// - Parameters:
  24. /// - collection: The list of messages to encode.
  25. /// - options: The JSONEncodingOptions to use.
  26. /// - Throws: ``JSONEncodingError`` if encoding fails.
  27. public static func jsonString<C: Collection>(
  28. from collection: C,
  29. options: JSONEncodingOptions = JSONEncodingOptions()
  30. ) throws -> String where C.Iterator.Element == Self {
  31. let data: [UInt8] = try jsonUTF8Bytes(from: collection, options: options)
  32. return String(bytes: data, encoding: .utf8)!
  33. }
  34. /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the messages.
  35. ///
  36. /// Unlike binary encoding, presence of required fields is not enforced when
  37. /// serializing to JSON.
  38. ///
  39. /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the messages.
  40. /// - Parameters:
  41. /// - collection: The list of messages to encode.
  42. /// - options: The JSONEncodingOptions to use.
  43. /// - Throws: ``JSONEncodingError`` if encoding fails.
  44. public static func jsonUTF8Bytes<C: Collection, Bytes: SwiftProtobufContiguousBytes>(
  45. from collection: C,
  46. options: JSONEncodingOptions = JSONEncodingOptions()
  47. ) throws -> Bytes where C.Iterator.Element == Self {
  48. var visitor = try JSONEncodingVisitor(type: Self.self, options: options)
  49. visitor.startArray()
  50. for message in collection {
  51. visitor.startArrayObject(message: message)
  52. try message.traverse(visitor: &visitor)
  53. visitor.endObject()
  54. }
  55. visitor.endArray()
  56. return Bytes(visitor.dataResult)
  57. }
  58. /// Creates a new array of messages by decoding the given string containing a
  59. /// serialized array of messages in JSON format.
  60. ///
  61. /// - Parameter jsonString: The JSON-formatted string to decode.
  62. /// - Parameter options: The JSONDecodingOptions to use.
  63. /// - Throws: ``JSONDecodingError`` if decoding fails.
  64. public static func array(
  65. fromJSONString jsonString: String,
  66. options: JSONDecodingOptions = JSONDecodingOptions()
  67. ) throws -> [Self] {
  68. try self.array(
  69. fromJSONString: jsonString,
  70. extensions: SimpleExtensionMap(),
  71. options: options
  72. )
  73. }
  74. /// Creates a new array of messages by decoding the given string containing a
  75. /// serialized array of messages in JSON format.
  76. ///
  77. /// - Parameter jsonString: The JSON-formatted string to decode.
  78. /// - Parameter extensions: The extension map to use with this decode
  79. /// - Parameter options: The JSONDecodingOptions to use.
  80. /// - Throws: ``JSONDecodingError`` if decoding fails.
  81. public static func array(
  82. fromJSONString jsonString: String,
  83. extensions: any ExtensionMap = SimpleExtensionMap(),
  84. options: JSONDecodingOptions = JSONDecodingOptions()
  85. ) throws -> [Self] {
  86. if jsonString.isEmpty {
  87. throw JSONDecodingError.truncated
  88. }
  89. if let data = jsonString.data(using: String.Encoding.utf8) {
  90. return try array(fromJSONUTF8Bytes: data, extensions: extensions, options: options)
  91. } else {
  92. throw JSONDecodingError.truncated
  93. }
  94. }
  95. /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes``
  96. /// containing a serialized array of messages in JSON format, interpreting the data as
  97. /// UTF-8 encoded text.
  98. ///
  99. /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
  100. /// as UTF-8 encoded text.
  101. /// - Parameter options: The JSONDecodingOptions to use.
  102. /// - Throws: ``JSONDecodingError`` if decoding fails.
  103. public static func array<Bytes: SwiftProtobufContiguousBytes>(
  104. fromJSONUTF8Bytes jsonUTF8Bytes: Bytes,
  105. options: JSONDecodingOptions = JSONDecodingOptions()
  106. ) throws -> [Self] {
  107. try self.array(
  108. fromJSONUTF8Bytes: jsonUTF8Bytes,
  109. extensions: SimpleExtensionMap(),
  110. options: options
  111. )
  112. }
  113. /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes``
  114. /// containing a serialized array of messages in JSON format, interpreting the data as
  115. /// UTF-8 encoded text.
  116. ///
  117. /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented
  118. /// as UTF-8 encoded text.
  119. /// - Parameter extensions: The extension map to use with this decode
  120. /// - Parameter options: The JSONDecodingOptions to use.
  121. /// - Throws: ``JSONDecodingError`` if decoding fails.
  122. public static func array<Bytes: SwiftProtobufContiguousBytes>(
  123. fromJSONUTF8Bytes jsonUTF8Bytes: Bytes,
  124. extensions: any ExtensionMap = SimpleExtensionMap(),
  125. options: JSONDecodingOptions = JSONDecodingOptions()
  126. ) throws -> [Self] {
  127. try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
  128. var array = [Self]()
  129. if body.count > 0 {
  130. var decoder = JSONDecoder(
  131. source: body,
  132. options: options,
  133. messageType: Self.self,
  134. extensions: extensions
  135. )
  136. try decoder.decodeRepeatedMessageField(value: &array)
  137. if !decoder.scanner.complete {
  138. throw JSONDecodingError.trailingGarbage
  139. }
  140. }
  141. return array
  142. }
  143. }
  144. }