Codable implementation which omits default values?

Here is a possible implementation.
typealias AnyEquatable = AnyHashable // TODO: implement AnyEquatable properly
typealias Dict = [AnyHashable: AnyEquatable]

extension Encodable {
    func jsonData(encoder: JSONEncoder? = nil) throws -> Data {
        try (encoder ?? JSONEncoder()).encode(self)
    }
}

extension Decodable {
    static func from(jsonData: Data, decoder: JSONDecoder? = nil) throws -> Self {
        try (decoder ?? JSONDecoder()).decode(self, from: jsonData)
    }
}

extension JSONEncoder {
    var writingOptions: JSONSerialization.WritingOptions {
        let opts = outputFormatting
        var v: JSONSerialization.WritingOptions = []
        v.insert(opts.contains(.prettyPrinted) ? .prettyPrinted : [])
        v.insert(opts.contains(.sortedKeys) ? .sortedKeys : [])
        v.insert(opts.contains(.withoutEscapingSlashes) ? .withoutEscapingSlashes : [])
        return v
    }
}

extension Data {
    func decodeJsonObject(options: JSONSerialization.ReadingOptions = []) throws -> Any {
        try JSONSerialization.jsonObject(with: self, options: options)
    }
    init(encodeJsonObject object: Any, options: JSONSerialization.WritingOptions = []) throws {
        self = try JSONSerialization.data(withJSONObject: object, options: options)
    }
}

extension JSONEncoder {
    func encode<T: Encodable>(_ value: T, default defValue: T) throws -> Data {
        let writingOptions = self.writingOptions
        var data = try value.jsonData(encoder: self)
        let dict = try data.decodeJsonObject()
        let defData = try defValue.jsonData(encoder: self)
        let defDict = try defData.decodeJsonObject()
        
        guard var dict = dict as? Dict, let defDict = defDict as? Dict else {
            // TODO: use EncodingError
            throw NSError(domain: "JSON", code: -1, userInfo: nil)
        }
        if dict.removeMatchingValues(defDict) {
            data = try Data(encodeJsonObject: dict, options: writingOptions)
        }
        return data
    }
}

extension JSONDecoder {
    func decode<T: Codable>(_ type: T.Type, default defValue: T, from data: Data) throws -> T {
        let dict = try data.decodeJsonObject()
        let defData = try defValue.jsonData()
        let defDict = try defData.decodeJsonObject()
        
        guard var dict = dict as? Dict, let defDict = defDict as? Dict else {
            // TODO: use DecodingError
            throw NSError(domain: "JSON", code: -1, userInfo: nil)
        }
        _ = dict.addMissingValues(defDict)
        let newData = try Data(encodeJsonObject: dict)
        return try type.from(jsonData: newData, decoder: self)
    }
}


extension Dictionary {
    mutating func removeMatchingValues(_ dict: Self) -> Bool {
        var changed = false
        dict.forEach { key, defVal in
            if let val = self[key] {
                if let val = val as? AnyEquatable, let defVal = defVal as? AnyEquatable, val == defVal {
                    self[key] = nil
                    changed = true
                } else if var val = val as? Dict, let defVal = defVal as? Dict {
                    if val.removeMatchingValues(defVal) {
                        changed = true
                        self[key] = (val as! Value)
                    }
                }
            }
        }
        return changed
    }
    
    mutating func addMissingValues(_ dict: Self) -> Bool {
        var changed = false
        dict.forEach { key, defVal in
            if let val = self[key] {
                if var val = val as? Dict, let defVal = defVal as? Dict {
                    if val.addMissingValues(defVal) {
                        changed = true
                        self[key] = (val as! Value)
                    }
                }
            } else {
                self[key] = defVal
                changed = true
            }
        }
        return changed
    }
}

This implementation exposes a custom 'JSONEncoder().encode(val, default: defVal)' and ''JSONDecoder().decode(S.self, default: defVal, from: data)" that takes additional "default" value parameter: values missing in JSON are replaced with the corresponding default values and likewise the encoded data doesn't contain default values.

struct S: Codable {
    var x: Int = 100
    var y: String = "hello"
    var a: A = A()
    
    // purposely not using Equatable for this test so it doesn't affect things
    func eq(_ other: Self) -> Bool {
        x == other.x && y == other.y && a.eq(other.a)
    }
}

struct A: Codable {
    var a: Int = 300
    var b: String = "world"
    
    // purposely not using Equatable for this test so it doesn't affect things
    func eq(_ other: Self) -> Bool {
        a == other.a && b == other.b
    }
}

func testValue(_ val: S, default defVal: S, encoder: JSONEncoder, decoder: JSONDecoder) {
    let data = try! encoder.encode(val, default: defVal)
    let s = String(data: data, encoding: .utf8)!
    print("json: \(s.replacingOccurrences(of: "\\\"", with: "\""))")
    let val2 = try! decoder.decode(S.self, default: defVal, from: data)
    assert(val2.eq(val))
}

func test() {
    let encoder = JSONEncoder()
    let decoder = JSONDecoder()
    
    let def = S() // default value
    testValue(S(x: 1, y: "2", a: A(a: 3, b: "4")), default: def, encoder: encoder, decoder: decoder)
    testValue(S(x: 100, y: "hello", a: A(a: 300, b: "world")), default: def, encoder: encoder, decoder: decoder)
    testValue(S(x: 1, y: "hello", a: A(a: 3, b: "4")), default: def, encoder: encoder, decoder: decoder)
    testValue(S(x: 1, y: "hello", a: A(a: 3, b: "world")), default: def, encoder: encoder, decoder: decoder)
    testValue(S(x: 100, y: "hello", a: A(a: 300, b: "good")), default: def, encoder: encoder, decoder: decoder)
    print("done")
}

test()

outputs:
json: {"x":1,"y":"2","a":{"a":3,"b":"4"}}
json: {}
json: {"x":1,"a":{"b":"4","a":3}}
json: {"x":1,"a":{"a":3}}
json: {"a":{"b":"good"}}

As you can see this implementation generates compact JSON's without default values and can recreate the original values provided "decode" is called with the same default value parameter as "encode". The code is highly untested, so use on your own risk. Also note the TODO's in code (non critical but worth addressing) IRT "AnyEquatable" and "EncodingError / DecodingError".