Decode json with dynamic coding key using Codable?
Example of Json I need to decode. In "text" key we have [String: String] dict. And quantity of elements is in "count". How should I decode it properly?
{
"name": "LoremIpsum",
"index": "1",
"text": {
"text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
"text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
"text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}
My Codable model:
class Text: Codable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?
init() {
name = ""
index = 0
count = 0
texts =
}
init(name: String,
index: Int,
count: Int,
texts: [String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
} <---- also why do I need this method?
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
for i in (1...self.count!) {
self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_(i)") ?? .text))
}
}
}
And I decode it with:
if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
print("jsonData:(jsonObj)")
} catch let error {
print("parse error: (error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
But I got parse error
parse error: The data couldn’t be read because it is missing.
What is wrong with my code? Is it a good way to decode such dynamic coding keys?
json swift decode codable
add a comment |
Example of Json I need to decode. In "text" key we have [String: String] dict. And quantity of elements is in "count". How should I decode it properly?
{
"name": "LoremIpsum",
"index": "1",
"text": {
"text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
"text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
"text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}
My Codable model:
class Text: Codable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?
init() {
name = ""
index = 0
count = 0
texts =
}
init(name: String,
index: Int,
count: Int,
texts: [String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
} <---- also why do I need this method?
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
for i in (1...self.count!) {
self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_(i)") ?? .text))
}
}
}
And I decode it with:
if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
print("jsonData:(jsonObj)")
} catch let error {
print("parse error: (error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
But I got parse error
parse error: The data couldn’t be read because it is missing.
What is wrong with my code? Is it a good way to decode such dynamic coding keys?
json swift decode codable
add a comment |
Example of Json I need to decode. In "text" key we have [String: String] dict. And quantity of elements is in "count". How should I decode it properly?
{
"name": "LoremIpsum",
"index": "1",
"text": {
"text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
"text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
"text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}
My Codable model:
class Text: Codable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?
init() {
name = ""
index = 0
count = 0
texts =
}
init(name: String,
index: Int,
count: Int,
texts: [String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
} <---- also why do I need this method?
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
for i in (1...self.count!) {
self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_(i)") ?? .text))
}
}
}
And I decode it with:
if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
print("jsonData:(jsonObj)")
} catch let error {
print("parse error: (error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
But I got parse error
parse error: The data couldn’t be read because it is missing.
What is wrong with my code? Is it a good way to decode such dynamic coding keys?
json swift decode codable
Example of Json I need to decode. In "text" key we have [String: String] dict. And quantity of elements is in "count". How should I decode it properly?
{
"name": "LoremIpsum",
"index": "1",
"text": {
"text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
"text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
"text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}
My Codable model:
class Text: Codable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?
init() {
name = ""
index = 0
count = 0
texts =
}
init(name: String,
index: Int,
count: Int,
texts: [String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
} <---- also why do I need this method?
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
for i in (1...self.count!) {
self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_(i)") ?? .text))
}
}
}
And I decode it with:
if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
print("jsonData:(jsonObj)")
} catch let error {
print("parse error: (error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
But I got parse error
parse error: The data couldn’t be read because it is missing.
What is wrong with my code? Is it a good way to decode such dynamic coding keys?
json swift decode codable
json swift decode codable
asked Nov 13 '18 at 13:43
Said-Abdulla AtkaevSaid-Abdulla Atkaev
499410
499410
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
You need
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
add a comment |
Fix JSON
I suppose you want your index and count to be numbers. So replace this "index": "1"
and this "count": "4"
With this "index": 1
and this "count": 4
Class structure
With Codable protocol any of these CodingKeys and encode functions or required inits aren't neccessary. Also change texts property data format to [String: String]
So, replace your class like this:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
Decoding
For decoding your json use what you wrote above, this is correct
let object = try JSONDecoder().decode(Text.self, from: data)
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
|
show 2 more comments
The most important thing you need is this:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
This will convert the dictionary into an array of values. While in principle JSON values are not ordered, allKeys
is ordered (because it's a serialized protocol).
You wouldn't need the encode method if you conformed only to Decodable
rather than Codable
. Also, most of your optionals are not really optionals.
Putting those things together, you would have this:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = ) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53282380%2fdecode-json-with-dynamic-coding-key-using-codable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
You need
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
add a comment |
You need
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
add a comment |
You need
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
You need
struct Root: Codable {
let name, index,count: String
let text: [String:String]
}
--
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
edited Nov 13 '18 at 13:56
answered Nov 13 '18 at 13:55
Sh_KhanSh_Khan
40.5k51125
40.5k51125
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
add a comment |
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
The thing is that there can be text5, text6 and textN Depending on "count"
– Said-Abdulla Atkaev
Nov 13 '18 at 13:55
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev use only that struct and the decode should work
– Sh_Khan
Nov 13 '18 at 13:57
add a comment |
Fix JSON
I suppose you want your index and count to be numbers. So replace this "index": "1"
and this "count": "4"
With this "index": 1
and this "count": 4
Class structure
With Codable protocol any of these CodingKeys and encode functions or required inits aren't neccessary. Also change texts property data format to [String: String]
So, replace your class like this:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
Decoding
For decoding your json use what you wrote above, this is correct
let object = try JSONDecoder().decode(Text.self, from: data)
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
|
show 2 more comments
Fix JSON
I suppose you want your index and count to be numbers. So replace this "index": "1"
and this "count": "4"
With this "index": 1
and this "count": 4
Class structure
With Codable protocol any of these CodingKeys and encode functions or required inits aren't neccessary. Also change texts property data format to [String: String]
So, replace your class like this:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
Decoding
For decoding your json use what you wrote above, this is correct
let object = try JSONDecoder().decode(Text.self, from: data)
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
|
show 2 more comments
Fix JSON
I suppose you want your index and count to be numbers. So replace this "index": "1"
and this "count": "4"
With this "index": 1
and this "count": 4
Class structure
With Codable protocol any of these CodingKeys and encode functions or required inits aren't neccessary. Also change texts property data format to [String: String]
So, replace your class like this:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
Decoding
For decoding your json use what you wrote above, this is correct
let object = try JSONDecoder().decode(Text.self, from: data)
Fix JSON
I suppose you want your index and count to be numbers. So replace this "index": "1"
and this "count": "4"
With this "index": 1
and this "count": 4
Class structure
With Codable protocol any of these CodingKeys and encode functions or required inits aren't neccessary. Also change texts property data format to [String: String]
So, replace your class like this:
class Text: Codable {
public var name: String
public var index: Int
public var count: Int
public var texts: [String: String]
init(name: String, index: Int, count: Int, texts: [String: String]) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
}
Decoding
For decoding your json use what you wrote above, this is correct
let object = try JSONDecoder().decode(Text.self, from: data)
edited Nov 13 '18 at 14:05
answered Nov 13 '18 at 13:46
Robert DreslerRobert Dresler
4,9491526
4,9491526
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
|
show 2 more comments
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
So how should I decode this json?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:50
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
@Said-AbdullaAtkaev Read edited answer. You were decoding your json right, but your class was wrong.
– Robert Dresler
Nov 13 '18 at 13:53
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
index and count are not ints plus texts is a dictionary not array
– Sh_Khan
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
I need to remove all inits and also "init(from decoder: Decoder)" ?
– Said-Abdulla Atkaev
Nov 13 '18 at 13:57
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
@Said-AbdullaAtkaev if you want to, you can keep your init(name: String....
– Robert Dresler
Nov 13 '18 at 13:59
|
show 2 more comments
The most important thing you need is this:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
This will convert the dictionary into an array of values. While in principle JSON values are not ordered, allKeys
is ordered (because it's a serialized protocol).
You wouldn't need the encode method if you conformed only to Decodable
rather than Codable
. Also, most of your optionals are not really optionals.
Putting those things together, you would have this:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = ) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
add a comment |
The most important thing you need is this:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
This will convert the dictionary into an array of values. While in principle JSON values are not ordered, allKeys
is ordered (because it's a serialized protocol).
You wouldn't need the encode method if you conformed only to Decodable
rather than Codable
. Also, most of your optionals are not really optionals.
Putting those things together, you would have this:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = ) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
add a comment |
The most important thing you need is this:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
This will convert the dictionary into an array of values. While in principle JSON values are not ordered, allKeys
is ordered (because it's a serialized protocol).
You wouldn't need the encode method if you conformed only to Decodable
rather than Codable
. Also, most of your optionals are not really optionals.
Putting those things together, you would have this:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = ) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
The most important thing you need is this:
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
This will convert the dictionary into an array of values. While in principle JSON values are not ordered, allKeys
is ordered (because it's a serialized protocol).
You wouldn't need the encode method if you conformed only to Decodable
rather than Codable
. Also, most of your optionals are not really optionals.
Putting those things together, you would have this:
class Text: Decodable {
private enum CodingKeys: String, CodingKey {
case name, index, count, text
}
public var name: String
public var index: Int
public var count: Int
public var texts: [String]
init(name: String = "",
index: Int = 0,
count: Int = 0,
texts: [String] = ) {
self.name = name
self.index = index
self.count = count
self.texts = texts
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
}
}
let jsonObj = try JSONDecoder().decode(Text.self, from: data)
answered Nov 13 '18 at 14:08
Rob NapierRob Napier
200k28294420
200k28294420
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
add a comment |
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
I think this should be a server response fix not a local code
– Sh_Khan
Nov 13 '18 at 14:11
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53282380%2fdecode-json-with-dynamic-coding-key-using-codable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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