2

Given the classes:

class ComplementApp: Codable{
    let name: String
    let idSpring: String
}

class MasterClass: Encodable{
    let complement: ComplementApp
    ///Other propierties
}

I want to get:

//Where "Some ID" is the value of complement.idSpring
{
   complement: "Some ID"
   //Plus the other properties
}

Not

{
   complement: {
      name: "Some Name",
      idSpring: "Some ID"
   }
   //Plus other properties
}

Which is the default. I know that I can do it throw encode function and CodingKeys in MasterClass, but I have like 20 other variables, and I should add 19 extra keys. Can I achieve this implementing CodingKeys in ComplementApp?

1 Answers1

6

You can achieve this via a custom encode(to:) implementation:

class ComplementApp: Codable {
    let name: String
    let idSpring: String

    func encode(to coder: Encoder) throws {
        var container = coder.singleValueContainer()
        try container.encode(idSpring)
    }
}

Using singleValueContainer will result in your object being encoded as a single value instead of a JSON object. And you don't have to touch the outer class.

Cristik
  • 30,989
  • 25
  • 91
  • 127
  • Needless to say, `init(from:)` can also be implemented in a similar manner. – Nicolas Miari Aug 14 '22 at 05:19
  • @NicolasMiari ofcourse, however I would advise against it, at least for this particular scenario, as decoding such objects would result in possible invalid values. – Cristik Aug 14 '22 at 05:31
  • I think it's perfectly valid in a controlled environment where you **expect** the raw string to be convertible to your custom type. And there's a reason the Codable initializer throws. – Nicolas Miari Aug 14 '22 at 06:13
  • It depends, @NicolasMiari, if you're always encoding to a string, and decoding from a string, then you don't need the extra struct to begin with. – Cristik Aug 14 '22 at 11:59
  • My case is a bit particular I guess. I'm encoding a strongly typed, opaque "Identifier" struct that internally uses a UUID, but that is an implementation type that client code shouldn't care about. I want to encode/decode it using the `uuidString` valueof the wrapped UUID, so that in the serialized JSON it can act as a key to the objects it identifies. In any case, thanks for the answer! I wasn't even aware of the existence of `singleValueContainer()`. – Nicolas Miari Aug 15 '22 at 00:14
  • 1
    @NicolasMiari yeah, in your case it makes perfect sense to encode/decode to/from different data types. On the other side, the struct from this question has additional fields besides the one being encoded, so also omitting them when decoding makes those fields a litle bit useless. – Cristik Aug 15 '22 at 04:04