Yams Documentation

repository·main·Indexed 21 days ago

https://github.com/jpsim/yams

A Swift YAML parser built on top of LibYAML. Yams provides Codable support via YAMLEncoder and YAMLDecoder, Swift Standard Library type conversion using Yams.dump and Yams.load, and a native Node representation for advanced manipulation. It supports installation via Swift Package Manager, CMake, and Bazel.

Tokens
3.5K
Snippets
12
Records
13
Agent score
29%

What's inside Yams

  1. Customize parsing with a custom Constructor

    main

    You can modify how Yams interprets specific YAML tags by providing a custom Constructor. This is done by creating a new Constructor instance with a modified defaultScalarMap. This allows you to override default behaviors, such as restricting boolean parsing to only true and false literals.

    import Yams
    
    extension Constructor {
      public static func withBoolAsTrueFalse() -> Constructor {
        var map = defaultScalarMap
        map[.bool] = Bool.constructUsingOnlyTrueAndFalse
        return Constructor(map)
      }
    }
    
    // Usage:
    let yamlString = """
      - true
      - on
      - off
      - false
      """
    if let array = try? Yams.load(yaml: yamlString, .default, .withBoolAsTrueFalse()) as? [Any] {
      print(array)
    }
    // Prints: [true, "on", "off", false]
  2. Convert between YAML and other formats

    main
    Yams conforms to Swift's Codable protocol and provides its own YAMLDecoder and YAMLEncoder. This allows you to easily convert data between YAML and other formats that support Codable, such as JSON or Plist.
  3. Expand environment variables during parsing

    main

    To support environment variable expansion (e.g., ${VAR}) within YAML strings, create a custom Constructor that maps the .str tag to a function that performs string replacement using a provided dictionary.

    import Yams
    
    extension Constructor {
      public static func withEnv(_ env: [String: String]) -> Constructor {
        var map = defaultScalarMap
        map[.str] = String.constructExpandingEnvVars(env: env)
        return Constructor(map)
      }
    }
    
    // Usage:
    let yamlString = """
      - first
      - ${SECOND}
      - SECOND
      """
    let env = ["SECOND": "2"]
    if let array = try? Yams.load(yaml: yamlString, .default, .withEnv(env)) as? [String] {
      print(array)
    }
    // Prints: ["first", "2", "SECOND"]
  4. Install Yams via CMake

    main

    Building Yams requires CMake 3.17.2+ and Ninja 1.9.0+.

    For Apple Platforms (macOS, iOS, tvOS, watchOS)

    Foundation is included in the SDK, so you only need to run:

    cmake -B /path/to/build -G Ninja -S /path/to/yams -DCMAKE_BUILD_TYPE=Release
    cmake --build /path/to/build

    For Non-Apple Platforms

    You must provide a path to a built Foundation library using -DFoundation_DIR:

    cmake -B /path/to/build -G Ninja -S /path/to/yams -DCMAKE_BUILD_TYPE=Release -DFoundation_DIR=/path/to/foundation/build/cmake/modules
    cmake --build /path/to/build
  5. Install Yams via Bazel

    main

    Add the following to your WORKSPACE file. Ensure you replace SOME_SHA with the desired git commit SHA.

    YAMS_GIT_SHA = "SOME_SHA"
    http_archive(
        name = "com_github_jpsim_yams",
        urls = [
            "https://github.com/jpsim/Yams/archive/%s.zip" % YAMS_GIT_SHA,
        ],
        strip_prefix = "Yams-%s" % YAMS_GIT_SHA,
    )
  6. Install Yams via Swift Package Manager

    main

    To add Yams to your Swift project, add the following dependency to your Package.swift file:

    .package(url: "https://github.com/jpsim/Yams.git", from: "6.2.2")
  7. Convert Swift Standard Library types to and from YAML

    main

    Use Yams.dump(object:) and Yams.load(yaml:) to work with standard types like Dictionary, Array, String, etc. This method uses type inference via regular expressions and has higher computational overhead during decoding.

    • Encoding: Yams.dump(object:) produces a YAML String from a Swift object.
    • Decoding: Yams.load(yaml:) produces an Any instance from a YAML String.
    // Dictionary example
    let dictionary: [String: Any] = ["key": "value"]
    let mapYAML: String = try Yams.dump(object: dictionary)
    let loadedDictionary = try Yams.load(yaml: mapYAML) as? [String: Any]
    
    // Array example
    let array: [Int] = [1, 2, 3]
    let sequenceYAML: String = try Yams.dump(object: array)
    let loadedArray: [Int]? = try Yams.load(yaml: sequenceYAML) as? [Int]
    // [String: Any]
    let dictionary: [String: Any] = ["key": "value"]
    let mapYAML: String = try Yams.dump(object: dictionary)
    mapYAML == """\nkey: value\n\n"""
    let loadedDictionary = try Yams.load(yaml: mapYAML) as? [String: Any]
    
    // [Any]
    let array: [Int] = [1, 2, 3]
    let sequenceYAML: String = try Yams.dump(object: array)
    sequenceYAML == """\n- 1\n- 2\n- 3\n\n"""
    let loadedArray: [Int]? = try Yams.load(yaml: sequenceYAML) as? [Int]
    
    // Any
    let string = "string"
    let scalarYAML: String = try Yams.dump(object: string)
    scalarYAML == """\nstring\n\n"""
    let loadedString: String? = try Yams.load(yaml: scalarYAML) as? String
  8. Convert YAML to NSMutableDictionary or NSMutableArray

    main

    To work with mutable Cocoa-style collections, use a custom Constructor with Yams.load(yaml:options:constructor:).

    let yaml = """
    names:
      - Alice
      - Bob
    """
    
    let constructor = Constructor(Constructor.defaultScalarMap,
                                  Constructor.nsMutableMappingMap,
                                  Constructor.nsMutableSequenceMap)
    
    let result = try Yams.load(yaml: yaml, .default, constructor) as? NSMutableDictionary
    let names = result?["names"] as? NSMutableArray
    // names -> (Alice, Bob)
    let yaml = """
    names:
      - Alice
      - Bob
    """
    
    let constructor = Constructor(Constructor.defaultScalarMap,
                                  Constructor.nsMutableMappingMap,
                                  Constructor.nsMutableSequenceMap)
    
    let result = try Yams.load(yaml: yaml, .default, constructor) as? NSMutableDictionary
    let names = result?["names"] as? NSMutableArray
    print(names ?? "No data") // -> (Alice, Bob)
  9. Use Yams.Node for advanced YAML manipulation

    main

    Yams.Node is the native representation of YAML nodes. It allows for fine-grained control over the YAML format, such as customizing styles (e.g., flow vs block style).

    • Encoding: Yams.serialize(node:) produces a YAML String from a Node.
    • Decoding: Yams.compose(yaml:) produces a Node from a YAML String.
    var map: Yams.Node = [
        "array": [
            1, 2, 3
        ]
    ]
    map.mapping?.style = .flow
    map["array"]?.sequence?.style = .flow
    let yaml = try Yams.serialize(node: map)
    // yaml == "{array: [1, 2, 3]}"
    
    let node = try Yams.compose(yaml: yaml)
    // map == node
    var map: Yams.Node = [
        "array": [
            1, 2, 3
        ]
    ]
    map.mapping?.style = .flow
    map["array"]?.sequence?.style = .flow
    let yaml = try Yams.serialize(node: map)
    yaml == """\n{array: [1, 2, 3]}\n\n"""
    let node = try Yams.compose(yaml: yaml)
    map == node
  10. Consume YAML with Yams.load()

    main

    To parse a YAML string into Swift types, use Yams.load(yaml:). The resulting Node can be cast to standard Swift collections like Array or Dictionary using conditional casting.

    import Yams
    
    let yamlString = """
      - a
      - b
      - c
      """
    do {
      let yamlNode = try Yams.load(yaml: yamlString)
      if let yamlArray = yamlNode as? [String] {
        print(yamlArray)
      }
    } catch {
      print("handle error: \(error)")
    }
  11. Convert Codable types to and from YAML

    main

    Use YAMLEncoder and YAMLDecoder for types conforming to Codable. This method has the lowest computational overhead.

    • Encoding: Use YAMLEncoder.encode(_:) to produce a YAML String from an Encodable instance.
    • Decoding: Use YAMLDecoder.decode(_:from:) to decode an instance from a YAML String or Data.

    Note: YAMLDecoder also conforms to TopLevelDecoder, making it compatible with Apple's Combine framework's .decode(type:decoder:) operator.

    import Foundation
    import Yams
    
    struct S: Codable {
        var p: String
    }
    
    let s = S(p: "test")
    let encoder = YAMLEncoder()
    let encodedYAML = try encoder.encode(s)
    // encodedYAML == "p: test\n\n"
    
    let decoder = YAMLDecoder()
    let decoded = try decoder.decode(S.self, from: encodedYAML)
    // s.p == decoded.p
  12. Emit YAML with Yams.serialize()

    main

    To convert a Swift object or a Node into a YAML string, use Yams.serialize(node:). You can customize the output style (e.g., block vs. flow) by modifying the style property on the Node's components before serialization.

    import Yams
    
    // Basic serialization
    do {
      let yamlString = try Yams.serialize(node: ["a", "b", "c"])
      print(yamlString)
    } catch {
      print("handle error: \(error)")
    }
    
    // Customizing style to flow (e.g., [a, b, c])
    var node: Node = ["a", "b", "c"]
    node.sequence?.style = .flow
    
    do {
      let yamlString = try Yams.serialize(node: node)
      print(yamlString)
    } catch {
      print("handle error: \(error)")
    }