Skip to content

Commit ec73c9e

Browse files
feat: adds JSON string initialization (#4)
1 parent 6bf106a commit ec73c9e

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

.github/FUNDING.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
github: ["kevinhermawan"]
2+
custom:
3+
[
4+
"https://buymeacoffee.com/kevinhermawan"
5+
]

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,67 @@ let productSchema = JSONSchema.object(
132132
)
133133
```
134134

135+
You can also create the same schema using a JSON string:
136+
137+
```swift
138+
do {
139+
let jsonString = """
140+
{
141+
"type": "object",
142+
"properties": {
143+
"id": {
144+
"type": "integer",
145+
"minimum": 1
146+
},
147+
"name": {
148+
"type": "string",
149+
"minLength": 1,
150+
"maxLength": 100
151+
},
152+
"price": {
153+
"type": "number",
154+
"minimum": 0.01,
155+
"exclusiveMaximum": 1000000
156+
},
157+
"tags": {
158+
"type": "array",
159+
"items": {
160+
"type": "string"
161+
},
162+
"uniqueItems": true
163+
},
164+
"settings": {
165+
"type": "object",
166+
"properties": {
167+
"inStock": {
168+
"type": "boolean"
169+
},
170+
"size": {
171+
"type": "string",
172+
"enum": ["small", "medium", "large"]
173+
}
174+
}
175+
}
176+
},
177+
"required": ["id", "name", "price"]
178+
}
179+
"""
180+
181+
let productSchema = try JSONSchema(jsonString: jsonString)
182+
} catch {
183+
print(String(describing: error))
184+
}
185+
```
186+
187+
## Donations
188+
189+
If you find `JSONSchema` helpful and would like to support its development, consider making a donation. Your contribution helps maintain the project and develop new features.
190+
191+
- [GitHub Sponsors](https://github.com/sponsors/kevinhermawan)
192+
- [Buy Me a Coffee](https://buymeacoffee.com/kevinhermawan)
193+
194+
Your support is greatly appreciated!
195+
135196
## Contributing
136197

137198
Contributions are welcome! Please open an issue or submit a pull request if you have any suggestions or improvements.

Sources/JSONSchema/Documentation.docc/Documentation.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,55 @@ let productSchema = JSONSchema.object(
104104
required: ["id", "name", "price"]
105105
)
106106
```
107+
108+
You can also create the same schema using a JSON string:
109+
110+
```swift
111+
do {
112+
let jsonString = """
113+
{
114+
"type": "object",
115+
"properties": {
116+
"id": {
117+
"type": "integer",
118+
"minimum": 1
119+
},
120+
"name": {
121+
"type": "string",
122+
"minLength": 1,
123+
"maxLength": 100
124+
},
125+
"price": {
126+
"type": "number",
127+
"minimum": 0.01,
128+
"exclusiveMaximum": 1000000
129+
},
130+
"tags": {
131+
"type": "array",
132+
"items": {
133+
"type": "string"
134+
},
135+
"uniqueItems": true
136+
},
137+
"settings": {
138+
"type": "object",
139+
"properties": {
140+
"inStock": {
141+
"type": "boolean"
142+
},
143+
"size": {
144+
"type": "string",
145+
"enum": ["small", "medium", "large"]
146+
}
147+
}
148+
}
149+
},
150+
"required": ["id", "name", "price"]
151+
}
152+
"""
153+
154+
let productSchema = try JSONSchema(jsonString: jsonString)
155+
} catch {
156+
print(String(describing: error))
157+
}
158+
```

Sources/JSONSchema/JSONSchema.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,29 @@ public class JSONSchema: Codable {
101101
private enum CodingKeys: String, CodingKey {
102102
case type, description, `enum`
103103
}
104+
105+
/// Creates a new instance of ``JSONSchema`` from a JSON string.
106+
public convenience init(jsonString: String) throws {
107+
guard let data = jsonString.data(using: .utf8) else {
108+
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Invalid UTF-8 string"))
109+
}
110+
111+
let decoder = JSONDecoder()
112+
let decodedData = try decoder.decode(JSONSchema.self, from: data)
113+
114+
self.init(from: decodedData)
115+
}
116+
117+
private init(from decodedSchema: JSONSchema) {
118+
self.type = decodedSchema.type
119+
self.description = decodedSchema.description
120+
self.arraySchema = decodedSchema.arraySchema
121+
self.booleanSchema = decodedSchema.booleanSchema
122+
self.enumSchema = decodedSchema.enumSchema
123+
self.integerSchema = decodedSchema.integerSchema
124+
self.nullSchema = decodedSchema.nullSchema
125+
self.numberSchema = decodedSchema.numberSchema
126+
self.objectSchema = decodedSchema.objectSchema
127+
self.stringSchema = decodedSchema.stringSchema
128+
}
104129
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// JSONStringTests.swift
3+
// JSONSchema
4+
//
5+
// Created by Kevin Hermawan on 9/28/24.
6+
//
7+
8+
import XCTest
9+
@testable import JSONSchema
10+
11+
final class JSONStringTests: XCTestCase {
12+
func testValidJSON() throws {
13+
let jsonString = """
14+
{
15+
"type": "object",
16+
"properties": {
17+
"location": {
18+
"type": "string",
19+
"description": "The location to get the weather for, e.g. Bogor, Indonesia"
20+
},
21+
"format": {
22+
"type": "string",
23+
"description": "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'",
24+
"enum": ["celsius", "fahrenheit"]
25+
}
26+
},
27+
"required": ["location", "format"]
28+
}
29+
"""
30+
31+
let schema = try JSONSchema(jsonString: jsonString)
32+
XCTAssertEqual(schema.type, .object)
33+
XCTAssertNil(schema.description)
34+
35+
let objectSchema = schema.objectSchema
36+
XCTAssertNotNil(objectSchema)
37+
38+
let properties = objectSchema?.properties
39+
XCTAssertEqual(properties?.count, 2)
40+
41+
let locationSchema = properties?["location"]
42+
XCTAssertEqual(locationSchema?.type, .string)
43+
XCTAssertEqual(locationSchema?.description, "The location to get the weather for, e.g. Bogor, Indonesia")
44+
45+
let formatSchema = properties?["format"]
46+
XCTAssertEqual(formatSchema?.description, "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'")
47+
XCTAssertEqual(formatSchema?.enumSchema?.values, [.string("celsius"), .string("fahrenheit")])
48+
49+
XCTAssertEqual(objectSchema?.required, ["location", "format"])
50+
}
51+
52+
func testInvalidJSON() {
53+
let invalidJsonString = "{ invalid json }"
54+
55+
XCTAssertThrowsError(try JSONSchema(jsonString: invalidJsonString)) { error in
56+
XCTAssertTrue(error is DecodingError)
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)