Decode custom JSON with Decodable + Realm Swift












0















From the server I have a big JSON returned that looks something like this:



{
"id": "123",
"status": "ok",
"person": {
"administration": {
"name": "John"
}
},
"company": {
"name": "Test"
}
}


I have a struct:



struct Info: Decodable, Object {
let id: String
let status: String
let personName: String
let companyName: String
}


It conforms to Decodable protocol and also is a Object (Realm entity).
My question is: Am I able somehow to decode the name of the person in personName? Something like person.administration.name.

I want the end Realm Object, to be a flat one and mostly all of the fields are strings.

Should I create separate structs for Person/Company without being Realm Objects and in decode method to set the corresponding value to "personName"?



let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)









share|improve this question



























    0















    From the server I have a big JSON returned that looks something like this:



    {
    "id": "123",
    "status": "ok",
    "person": {
    "administration": {
    "name": "John"
    }
    },
    "company": {
    "name": "Test"
    }
    }


    I have a struct:



    struct Info: Decodable, Object {
    let id: String
    let status: String
    let personName: String
    let companyName: String
    }


    It conforms to Decodable protocol and also is a Object (Realm entity).
    My question is: Am I able somehow to decode the name of the person in personName? Something like person.administration.name.

    I want the end Realm Object, to be a flat one and mostly all of the fields are strings.

    Should I create separate structs for Person/Company without being Realm Objects and in decode method to set the corresponding value to "personName"?



    let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)









    share|improve this question

























      0












      0








      0


      1






      From the server I have a big JSON returned that looks something like this:



      {
      "id": "123",
      "status": "ok",
      "person": {
      "administration": {
      "name": "John"
      }
      },
      "company": {
      "name": "Test"
      }
      }


      I have a struct:



      struct Info: Decodable, Object {
      let id: String
      let status: String
      let personName: String
      let companyName: String
      }


      It conforms to Decodable protocol and also is a Object (Realm entity).
      My question is: Am I able somehow to decode the name of the person in personName? Something like person.administration.name.

      I want the end Realm Object, to be a flat one and mostly all of the fields are strings.

      Should I create separate structs for Person/Company without being Realm Objects and in decode method to set the corresponding value to "personName"?



      let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)









      share|improve this question














      From the server I have a big JSON returned that looks something like this:



      {
      "id": "123",
      "status": "ok",
      "person": {
      "administration": {
      "name": "John"
      }
      },
      "company": {
      "name": "Test"
      }
      }


      I have a struct:



      struct Info: Decodable, Object {
      let id: String
      let status: String
      let personName: String
      let companyName: String
      }


      It conforms to Decodable protocol and also is a Object (Realm entity).
      My question is: Am I able somehow to decode the name of the person in personName? Something like person.administration.name.

      I want the end Realm Object, to be a flat one and mostly all of the fields are strings.

      Should I create separate structs for Person/Company without being Realm Objects and in decode method to set the corresponding value to "personName"?



      let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)






      ios swift realm decodable






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 22 '18 at 15:52









      SlavchoSlavcho

      2,53632241




      2,53632241
























          2 Answers
          2






          active

          oldest

          votes


















          1














          You can simply use containers to decode nested data with Decodable, i.e.



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          enum CodingKeys: String, CodingKey {
          case id, status
          case person, administration
          case company
          case name
          }

          init(from decoder: Decoder) throws {
          let values = try decoder.container(keyedBy: CodingKeys.self)
          id = try values.decode(String.self, forKey: .id)
          status = try values.decode(String.self, forKey: .status)

          //Decoding personName
          let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
          let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
          personName = try administration.decode(String.self, forKey: .name)

          //Decoding companyName
          let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
          companyName = try company.decode(String.self, forKey: .name)
          }
          }


          Example:



          I've decoded the JSON you provided above, i.e.



          if let data = json.data(using: .utf8) {
          let info = try? JSONDecoder().decode(Info.self, from: data)
          print(info)
          }


          The output it gives is:



          (id: "123", status: "ok", personName: "John", companyName: "Test")



          You can separate out the CodingKeys for all the different levels as per your wish. I kept them at the same level for simplicity.



          Suggestion: Try using the optional types with Codable. This is because the API response can be unexpected. And if you don't get any expected key-value pair, you might end up getting a nil while creating the object.






          share|improve this answer


























          • Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

            – Slavcho
            Nov 22 '18 at 16:40






          • 1





            Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

            – PGDev
            Nov 22 '18 at 16:42



















          1














          It is best practice to separate transport types you're parsing your JSON into and types to represent object in the storage.



          But if you want to use this combined types you should do something like this:



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          // JSON root keys
          private enum RootKeys: String, CodingKey {
          case id, status, person, company
          }
          // Keys for "person" nested "object"
          private enum PersonKeys: String, CodingKey {
          case administration
          }
          // Keys for "administration" and "company"
          private enum NamedKeys: String, CodingKey {
          case name
          }

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

          self.id = try container.decode(String.self, forKey: .id)
          self.status = try container.decode(String.self, forKey: .status)

          let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
          let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
          self.personName = try administrationContainer.decode(String.self, forKey: .name)

          let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
          self.companyName = try companyContainer.decode(String.self, forKey: .name)
          }
          }




          I separated keys into three different CodingKey types for some type safety, and to prevent accidental mixup.






          share|improve this answer
























          • Thank you for the answer :)

            – Slavcho
            Nov 22 '18 at 16:41











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53434510%2fdecode-custom-json-with-decodable-realm-swift%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          You can simply use containers to decode nested data with Decodable, i.e.



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          enum CodingKeys: String, CodingKey {
          case id, status
          case person, administration
          case company
          case name
          }

          init(from decoder: Decoder) throws {
          let values = try decoder.container(keyedBy: CodingKeys.self)
          id = try values.decode(String.self, forKey: .id)
          status = try values.decode(String.self, forKey: .status)

          //Decoding personName
          let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
          let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
          personName = try administration.decode(String.self, forKey: .name)

          //Decoding companyName
          let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
          companyName = try company.decode(String.self, forKey: .name)
          }
          }


          Example:



          I've decoded the JSON you provided above, i.e.



          if let data = json.data(using: .utf8) {
          let info = try? JSONDecoder().decode(Info.self, from: data)
          print(info)
          }


          The output it gives is:



          (id: "123", status: "ok", personName: "John", companyName: "Test")



          You can separate out the CodingKeys for all the different levels as per your wish. I kept them at the same level for simplicity.



          Suggestion: Try using the optional types with Codable. This is because the API response can be unexpected. And if you don't get any expected key-value pair, you might end up getting a nil while creating the object.






          share|improve this answer


























          • Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

            – Slavcho
            Nov 22 '18 at 16:40






          • 1





            Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

            – PGDev
            Nov 22 '18 at 16:42
















          1














          You can simply use containers to decode nested data with Decodable, i.e.



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          enum CodingKeys: String, CodingKey {
          case id, status
          case person, administration
          case company
          case name
          }

          init(from decoder: Decoder) throws {
          let values = try decoder.container(keyedBy: CodingKeys.self)
          id = try values.decode(String.self, forKey: .id)
          status = try values.decode(String.self, forKey: .status)

          //Decoding personName
          let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
          let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
          personName = try administration.decode(String.self, forKey: .name)

          //Decoding companyName
          let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
          companyName = try company.decode(String.self, forKey: .name)
          }
          }


          Example:



          I've decoded the JSON you provided above, i.e.



          if let data = json.data(using: .utf8) {
          let info = try? JSONDecoder().decode(Info.self, from: data)
          print(info)
          }


          The output it gives is:



          (id: "123", status: "ok", personName: "John", companyName: "Test")



          You can separate out the CodingKeys for all the different levels as per your wish. I kept them at the same level for simplicity.



          Suggestion: Try using the optional types with Codable. This is because the API response can be unexpected. And if you don't get any expected key-value pair, you might end up getting a nil while creating the object.






          share|improve this answer


























          • Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

            – Slavcho
            Nov 22 '18 at 16:40






          • 1





            Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

            – PGDev
            Nov 22 '18 at 16:42














          1












          1








          1







          You can simply use containers to decode nested data with Decodable, i.e.



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          enum CodingKeys: String, CodingKey {
          case id, status
          case person, administration
          case company
          case name
          }

          init(from decoder: Decoder) throws {
          let values = try decoder.container(keyedBy: CodingKeys.self)
          id = try values.decode(String.self, forKey: .id)
          status = try values.decode(String.self, forKey: .status)

          //Decoding personName
          let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
          let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
          personName = try administration.decode(String.self, forKey: .name)

          //Decoding companyName
          let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
          companyName = try company.decode(String.self, forKey: .name)
          }
          }


          Example:



          I've decoded the JSON you provided above, i.e.



          if let data = json.data(using: .utf8) {
          let info = try? JSONDecoder().decode(Info.self, from: data)
          print(info)
          }


          The output it gives is:



          (id: "123", status: "ok", personName: "John", companyName: "Test")



          You can separate out the CodingKeys for all the different levels as per your wish. I kept them at the same level for simplicity.



          Suggestion: Try using the optional types with Codable. This is because the API response can be unexpected. And if you don't get any expected key-value pair, you might end up getting a nil while creating the object.






          share|improve this answer















          You can simply use containers to decode nested data with Decodable, i.e.



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          enum CodingKeys: String, CodingKey {
          case id, status
          case person, administration
          case company
          case name
          }

          init(from decoder: Decoder) throws {
          let values = try decoder.container(keyedBy: CodingKeys.self)
          id = try values.decode(String.self, forKey: .id)
          status = try values.decode(String.self, forKey: .status)

          //Decoding personName
          let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
          let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
          personName = try administration.decode(String.self, forKey: .name)

          //Decoding companyName
          let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
          companyName = try company.decode(String.self, forKey: .name)
          }
          }


          Example:



          I've decoded the JSON you provided above, i.e.



          if let data = json.data(using: .utf8) {
          let info = try? JSONDecoder().decode(Info.self, from: data)
          print(info)
          }


          The output it gives is:



          (id: "123", status: "ok", personName: "John", companyName: "Test")



          You can separate out the CodingKeys for all the different levels as per your wish. I kept them at the same level for simplicity.



          Suggestion: Try using the optional types with Codable. This is because the API response can be unexpected. And if you don't get any expected key-value pair, you might end up getting a nil while creating the object.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 22 '18 at 16:38

























          answered Nov 22 '18 at 16:27









          PGDevPGDev

          6,48721246




          6,48721246













          • Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

            – Slavcho
            Nov 22 '18 at 16:40






          • 1





            Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

            – PGDev
            Nov 22 '18 at 16:42



















          • Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

            – Slavcho
            Nov 22 '18 at 16:40






          • 1





            Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

            – PGDev
            Nov 22 '18 at 16:42

















          Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

          – Slavcho
          Nov 22 '18 at 16:40





          Thanks. I was looking for values.nestedContainer. So what happens if "company" was an array of strings?

          – Slavcho
          Nov 22 '18 at 16:40




          1




          1





          Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

          – PGDev
          Nov 22 '18 at 16:42





          Use [String].self instead of String.self while decoding, i.e. try company.decode([String].self, forKey: .name)

          – PGDev
          Nov 22 '18 at 16:42













          1














          It is best practice to separate transport types you're parsing your JSON into and types to represent object in the storage.



          But if you want to use this combined types you should do something like this:



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          // JSON root keys
          private enum RootKeys: String, CodingKey {
          case id, status, person, company
          }
          // Keys for "person" nested "object"
          private enum PersonKeys: String, CodingKey {
          case administration
          }
          // Keys for "administration" and "company"
          private enum NamedKeys: String, CodingKey {
          case name
          }

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

          self.id = try container.decode(String.self, forKey: .id)
          self.status = try container.decode(String.self, forKey: .status)

          let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
          let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
          self.personName = try administrationContainer.decode(String.self, forKey: .name)

          let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
          self.companyName = try companyContainer.decode(String.self, forKey: .name)
          }
          }




          I separated keys into three different CodingKey types for some type safety, and to prevent accidental mixup.






          share|improve this answer
























          • Thank you for the answer :)

            – Slavcho
            Nov 22 '18 at 16:41
















          1














          It is best practice to separate transport types you're parsing your JSON into and types to represent object in the storage.



          But if you want to use this combined types you should do something like this:



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          // JSON root keys
          private enum RootKeys: String, CodingKey {
          case id, status, person, company
          }
          // Keys for "person" nested "object"
          private enum PersonKeys: String, CodingKey {
          case administration
          }
          // Keys for "administration" and "company"
          private enum NamedKeys: String, CodingKey {
          case name
          }

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

          self.id = try container.decode(String.self, forKey: .id)
          self.status = try container.decode(String.self, forKey: .status)

          let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
          let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
          self.personName = try administrationContainer.decode(String.self, forKey: .name)

          let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
          self.companyName = try companyContainer.decode(String.self, forKey: .name)
          }
          }




          I separated keys into three different CodingKey types for some type safety, and to prevent accidental mixup.






          share|improve this answer
























          • Thank you for the answer :)

            – Slavcho
            Nov 22 '18 at 16:41














          1












          1








          1







          It is best practice to separate transport types you're parsing your JSON into and types to represent object in the storage.



          But if you want to use this combined types you should do something like this:



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          // JSON root keys
          private enum RootKeys: String, CodingKey {
          case id, status, person, company
          }
          // Keys for "person" nested "object"
          private enum PersonKeys: String, CodingKey {
          case administration
          }
          // Keys for "administration" and "company"
          private enum NamedKeys: String, CodingKey {
          case name
          }

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

          self.id = try container.decode(String.self, forKey: .id)
          self.status = try container.decode(String.self, forKey: .status)

          let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
          let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
          self.personName = try administrationContainer.decode(String.self, forKey: .name)

          let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
          self.companyName = try companyContainer.decode(String.self, forKey: .name)
          }
          }




          I separated keys into three different CodingKey types for some type safety, and to prevent accidental mixup.






          share|improve this answer













          It is best practice to separate transport types you're parsing your JSON into and types to represent object in the storage.



          But if you want to use this combined types you should do something like this:



          struct Info: Decodable {
          let id: String
          let status: String
          let personName: String
          let companyName: String

          // JSON root keys
          private enum RootKeys: String, CodingKey {
          case id, status, person, company
          }
          // Keys for "person" nested "object"
          private enum PersonKeys: String, CodingKey {
          case administration
          }
          // Keys for "administration" and "company"
          private enum NamedKeys: String, CodingKey {
          case name
          }

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

          self.id = try container.decode(String.self, forKey: .id)
          self.status = try container.decode(String.self, forKey: .status)

          let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
          let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
          self.personName = try administrationContainer.decode(String.self, forKey: .name)

          let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
          self.companyName = try companyContainer.decode(String.self, forKey: .name)
          }
          }




          I separated keys into three different CodingKey types for some type safety, and to prevent accidental mixup.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 22 '18 at 16:29









          user28434user28434

          3,3671126




          3,3671126













          • Thank you for the answer :)

            – Slavcho
            Nov 22 '18 at 16:41



















          • Thank you for the answer :)

            – Slavcho
            Nov 22 '18 at 16:41

















          Thank you for the answer :)

          – Slavcho
          Nov 22 '18 at 16:41





          Thank you for the answer :)

          – Slavcho
          Nov 22 '18 at 16:41


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53434510%2fdecode-custom-json-with-decodable-realm-swift%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Tonle Sap (See)

          I get strange results when I access the Sqlitedatabase with Unity C# via XAMPP

          Guatemaltekische Davis-Cup-Mannschaft