Swift JSON library with Embedded Swift support.
A single implementation that compiles for regular Swift (macOS, Linux, iOS, WebAssembly) and Embedded Swift (bare-metal, wasm-embedded) with no Foundation, Codable, existentials, reflection or dynamic casts in the core.
import JSON
// literals
let json: JSON = [
"name": "Alsey",
"age": 28,
"admin": true,
"tags": ["swift", "embedded"]
]
// accessors
json["name"]?.stringValue // "Alsey"
json["tags"]?[0] // .string("swift")// parse from String, [UInt8] or Data
let value = try JSON(parsing: #"{"key": [1, 2.5, null]}"#)
// serialize
let compact = value.toString() // {"key":[1,2.5,null]}
let pretty = value.toString(options: [.prettyPrint, .sortedKeys])
let data = value.toData()Parsing throws typed errors (throws(JSONParseError)) with byte offset and reason.
Attach the @JSONCodable macro to a struct or class to generate JSONEncodable and JSONDecodable conformance from its stored properties:
@JSONCodable
struct Product {
let id: UUID
var name: String
var price: Double
var quantity: UInt16?
}
let product = try Product(from: JSON(parsing: string))
let string = product.encode().toString()The macro generates a CodingKeys enum, init(from:) and encode(). Optional properties are omitted when nil and decode as nil when absent or null. For structs the members are generated in an extension, so the compiler's memberwise initializer is preserved.
Macros are enabled by default and require swift-syntax at build time; pass SWIFTPM_ENABLE_MACROS=0 to build without them (required for Embedded Swift, where you hand-write the conformances instead).
Alternatively, conform your types to JSONEncodable and JSONDecodable with hand-written implementations:
struct Person: JSONEncodable, JSONDecodable {
let id: UUID
var name: String
var age: UInt8?
enum CodingKeys: String, CodingKey {
case id
case name
case age
}
init(from json: JSON) throws(JSONDecodeError) {
self.id = try json.decode(UUID.self, forKey: CodingKeys.id)
self.name = try json.decode(String.self, forKey: CodingKeys.name)
self.age = try json.decodeIfPresent(UInt8.self, forKey: CodingKeys.age)
}
func encode() -> JSON {
var json = JSON.object([:])
json.encode(id, forKey: CodingKeys.id)
json.encode(name, forKey: CodingKeys.name)
json.encodeIfPresent(age, forKey: CodingKeys.age)
return json
}
}
let person = try Person(from: JSON(parsing: string))
let string = person.encode().toString()Conformances are provided for Bool, String, all integer widths, Float, Double, Optional, Array, Dictionary<String, _>, RawRepresentable enums, and the Foundation types UUID, URL, Date, Data and Decimal.
The same module builds unmodified under Embedded Swift:
- Typed
throwseverywhere — noany Errorexistentials. - No
Codable— under Embedded, a drop-inCodingKeyprotocol is provided; declareenum CodingKeys: String, CodingKeyand the same source compiles in both modes. - Foundation types are provided by
swift-embedded-foundationon platforms withoutFoundationorFoundationEssentials.
Build with the Embedded WebAssembly SDK:
swift sdk install https://download.swift.org/swift-6.3.3-release/wasm-sdk/swift-6.3.3-RELEASE/swift-6.3.3-RELEASE_wasm.artifactbundle.tar.gz
./build-embedded.shEmbedded executables using this library must link the Swift toolchain's Unicode data tables — String's Hashable/Comparable conformances (used by [String: JSON] objects and sortedKeys) depend on Unicode normalization symbols that the Embedded stdlib does not embed by default:
swift build --swift-sdk <embedded-sdk> \
-Xlinker -L<toolchain>/usr/lib/swift/embedded/<target-triple> \
-Xlinker -lswiftUnicodeDataTablesThe library itself avoids the other common Embedded linking gap: Double.init(String) requires _swift_stdlib_strtod_clocale, which no Embedded runtime library provides, so the JSON parser converts floating point numbers with its own pure-Swift implementation under Embedded Swift.
Add to your Package.swift:
.package(url: "https://github.com/PureSwift/JSON.git", branch: "feature/embedded")