简体   繁体   中英

Custom encoding for nested structure

This question is about how to write Encodable definition for a custom nested struct I have. I have defined a JSON request struct as follows:

   //Define type of request - get or set
    enum ReqType: String, Codable {
        case get,set
    }
    //For get request, define what request we can make
  enum GetReq: String, Codable {
        case getPage,getOther
    }
    

    //Request structure - reqBody for now is set to GetReq type
    // to keep it simple, no sum type over GetReq and SetReq for now
    struct Req {
        var reqType: ReqType
        var reqBody: GetReq
        enum CodingKeys: String, CodingKey{
            case reqType
        }
    }
    
    //An incomplete attempt at encoding logic
    extension Req: Encodable {
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(reqBody, forKey: .reqType)
        }
    }

Now, given a request say Req(reqType: ReqType.get,reqBody: GetReq.getPage) , we want to encode it as {"get":"getPage"} . That is, key is the string value of ReqType enum, and value is the string value of GetReq enum. My code above can't encode string value of ReqType enum as key. So, it ends up encoding as {"reqType":"getPage"} .

Will appreciate help with fixing the code to achieve the encoding where the key is string value of the enum.

Since you want ReqType to be the key of the JSON, conform that to CodingKey , instead of Codable :

enum ReqType: String, CodingKey {
    case get,set
}

Now you can do

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: ReqType.self)
    try container.encode(reqBody, forKey: reqType)
}

In case you also need a Decodable implementation:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: ReqType.self)

    // picks the first valid ReqType, in case the JSON has multiple keys
    if let key = container.allKeys.first {
        reqBody = try container.decode(GetReq.self, forKey: key)
        reqType = key
    } else {
        throw DecodingError.valueNotFound(GetReq.self, .init(codingPath: container.codingPath, debugDescription: "No request body found!"))
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM