Add swift-structured-data to your package and decode JSON, YAML, and XML through one API.
Add the package to the dependencies in your Package.swift.
dependencies: [
.package(url: "https://github.com/no-problem-dev/swift-structured-data.git", from: "2.0.0"),
],Then add the products you need to a target.
targets: [
.target(
name: "MyTarget",
dependencies: [
.product(name: "StructuredDataCore", package: "swift-structured-data"),
.product(name: "JSONParsing", package: "swift-structured-data"),
.product(name: "YAMLParsing", package: "swift-structured-data"),
.product(name: "XMLCoding", package: "swift-structured-data"),
]
),
]The format libraries are independent of one another, so depend only on the ones you use. Each one is built on StructuredDataCore but does not re-export it, so any file that names a core type directly — StructuredValue, DecodingOptions, or a protocol such as StructuredDecoding — must import StructuredDataCore as well.
Every format parser produces a StructuredValue: an enum with six cases — .null, .bool, .number, .string, .array, and .object.
import StructuredDataCore
// Build a value from literals.
let value: StructuredValue = [
"name": "Alice",
"age": 30,
"active": true,
]
// Dynamic member lookup — never throws. A missing path surfaces as .null.
let name = value.name.string // "Alice"
let city = value.address.city.string // nil (missing path → .null → nil)
// Typed key-based accessors.
let age = value.int("age") // 30
let flag = value.bool("active") // trueimport StructuredDataCore
import JSONParsing
struct Article: Decodable {
var title: String
var viewCount: Int
}
let decoder = JSONDecoder(
decodingOptions: DecodingOptions(keyStrategy: .convertFromSnakeCase)
)
let article = try decoder.decode(Article.self, from: jsonData)Accepting any StructuredDecoding keeps your own code format-independent.
func parse<T: Decodable>(_ type: T.Type, from data: Data, decoder: any StructuredDecoding) throws -> T {
try decoder.decode(type, from: data)
}YAMLDecoder exposes the same StructuredDecoding protocol as JSONDecoder, so it drops into any place the JSON decoder already works.
import StructuredDataCore
import YAMLParsing
let decoder = YAMLDecoder(
decodingOptions: DecodingOptions(keyStrategy: .convertFromSnakeCase)
)
let config = try decoder.decode(AppConfig.self, from: yamlData)YAMLParser also reads multi-document streams.
let documents: [StructuredValue] = try YAMLParser().parseAll(yamlData)XML has a richer model of its own — XMLElement and XMLNode — that preserves attributes, mixed content, and CDATA.
import XMLCoding
// Parse an XML document.
let root = try XMLDocumentParser().parse(xmlData)
let version = root.attribute("version") // read an attribute
let items = root.firstElement(named: "items") // first matching child element
// Build XML with the result builder (for example, Anthropic prompt tags).
let prompt = XMLElement("prompt") {
XMLElement("system", text: "You are a data analyst.")
XMLElement("user") {
XMLElement("question", text: userQuestion)
}
}
let xmlString = prompt.rendered()XMLCoding deliberately stays outside the Codable bridge: there is no built-in projection from an XML tree to a StructuredValue. If you need one, write it in your application layer against the shape of your documents, or walk the tree directly.
| Situation | What to use |
|---|---|
| Ordinary REST or LLM JSON payloads | JSONDecoder |
| LLM token streams (partial JSON) | StreamingJSONParser |
| Configuration files | YAMLDecoder |
| Anthropic XML prompt tags | XMLDocumentParser and XMLBuilder |
| Raw intermediate values | JSONParser or YAMLParser directly |
| Format-independent decoding | any StructuredDecoding |