-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathJSONValue.swift
More file actions
95 lines (86 loc) · 3.06 KB
/
Copy pathJSONValue.swift
File metadata and controls
95 lines (86 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright 2026 Anthropic PBC
// SPDX-License-Identifier: Apache-2.0
import Foundation
/// Loosely-typed JSON for tool inputs/outputs and schemas, where the shape
/// is determined at runtime.
package enum JSONValue: Sendable, Hashable, Codable {
case null
case bool(Bool)
case number(Double)
case string(String)
case array([JSONValue])
case object([String: JSONValue])
package init(from decoder: Decoder) throws {
let c = try decoder.singleValueContainer()
if c.decodeNil() {
self = .null
return
}
if let v = try? c.decode(Bool.self) {
self = .bool(v)
return
}
if let v = try? c.decode(Double.self) {
self = .number(v)
return
}
if let v = try? c.decode(String.self) {
self = .string(v)
return
}
if let v = try? c.decode([JSONValue].self) {
self = .array(v)
return
}
if let v = try? c.decode([String: JSONValue].self) {
self = .object(v)
return
}
throw DecodingError.dataCorruptedError(in: c, debugDescription: "Unsupported JSON value")
}
package func encode(to encoder: Encoder) throws {
var c = encoder.singleValueContainer()
switch self {
case .null: try c.encodeNil()
case .bool(let v): try c.encode(v)
case .number(let v): try c.encode(v)
case .string(let v): try c.encode(v)
case .array(let v): try c.encode(v)
case .object(let v): try c.encode(v)
}
}
}
extension JSONValue {
/// The named field of an object; `nil` for a missing field or a non-object.
package subscript(field: String) -> JSONValue? {
if case .object(let fields) = self { fields[field] } else { nil }
}
/// Decodes the value into a `Decodable` type by round-tripping through
/// `Data` — payload shapes are small, so clarity wins over speed.
package func decoded<Value: Decodable>() -> Value? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return try? JSONDecoder().decode(Value.self, from: data)
}
package static func encoded(_ value: some Encodable) -> JSONValue? {
guard let data = try? JSONEncoder().encode(value) else { return nil }
return try? JSONDecoder().decode(JSONValue.self, from: data)
}
package static func parsed(_ json: String) -> JSONValue? {
try? JSONDecoder().decode(JSONValue.self, from: Data(json.utf8))
}
}
extension JSONValue: ExpressibleByNilLiteral, ExpressibleByBooleanLiteral,
ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral,
ExpressibleByStringLiteral, ExpressibleByArrayLiteral,
ExpressibleByDictionaryLiteral
{
package init(nilLiteral: ()) { self = .null }
package init(booleanLiteral value: Bool) { self = .bool(value) }
package init(integerLiteral value: Int) { self = .number(Double(value)) }
package init(floatLiteral value: Double) { self = .number(value) }
package init(stringLiteral value: String) { self = .string(value) }
package init(arrayLiteral elements: JSONValue...) { self = .array(elements) }
package init(dictionaryLiteral elements: (String, JSONValue)...) {
self = .object(.init(uniqueKeysWithValues: elements))
}
}