Contents

JSONEncoder.KeyEncodingStrategy.custom(_:)

A key encoding strategy defined by the closure you supply.

Declaration

@preconcurrency case custom(@Sendable ([any CodingKey]) -> any CodingKey)

Parameters

  • codingPath:

    A closure that provides the full path to the current encoding position, and returns a customized coding key.

Discussion

The value associated with this case is a closure you use to choose the names of keys in the encoded JSON object. During encoding, the closure executes once for each key in the Encodable value. The closure receives an array of CodingKey instances representing the sequence of keys needed to reach the value the encoder is currently encoding.

The example below shows how to encode the properties of the nested A, B, and C structures with custom logic that you specify in the closure value associated with the custom case.

struct A: Codable {
    var value: Int
    var b: B
    
    struct B: Codable {
        var value: Int
        var c: C
        
        struct C: Codable {
            var value: Int
        }
    }
}

let a: A = A(value: 1, b: .init(value: 2, c: .init(value: 3)))

print(a.b.c.value) // Prints "3"

let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let encodeAndPrint = { print(String(data: try encoder.encode(a), encoding: .utf8) ?? "String encoding error.") }

/// An implementation of CodingKey that's useful for combining and transforming keys as strings.
struct AnyKey: CodingKey {
    var stringValue: String
    var intValue: Int?
    
    init?(stringValue: String) {
        self.stringValue = stringValue
        self.intValue = nil
    }
    
    init?(intValue: Int) {
        self.stringValue = String(intValue)
        self.intValue = intValue
    }
}

In the next example, you use the AnyKey structure defined above to customize the encoding of the A, B, and C structures.

// Customize each `value` key to contain a dot-syntax path.
encoder.keyEncodingStrategy = .custom { keys in
    if keys.last!.stringValue == "value" {
        return AnyKey(stringValue: "a." + keys.map { key in
            key.stringValue
        }.joined(separator: "."))!
    } else {
        return keys.last!
    }
}

try encodeAndPrint()

Here’s the JSON object that results from the custom encoding above:

 {
     "a.value": 1,
     "b": {
         "a.b.value": 2,
         "c": {
            "a.b.c.value": 3
         }
     }
 }