我正在尝试解析此json:
{
"objName": "Stage",
"sounds": [{
"soundName": "pop",
"soundID": 0,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
}],
"costumes": [{
"costumeName": "backdrop1",
"baseLayerID": 6,
"baseLayerMD5": "b61b1077b0ea1931abee9dbbfa7903ff.png",
"bitmapResolution": 2,
"rotationCenterX": 480,
"rotationCenterY": 360
}],
"currentCostumeIndex": 0,
"penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
"penLayerID": 0,
"tempoBPM": 60,
"videoAlpha": 0.5,
"children": [{
"objName": "img",
"scripts": [[5, 5, [["whenGreenFlag"], ["setSizeTo:", 101], ["gotoX:y:", 0, 0]]]],
"sounds": [{
"soundName": "pop",
"soundID": 0,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
}],
"costumes": [{
"costumeName": "dotstickie3",
"baseLayerID": 1,
"baseLayerMD5": "bcea2c64c030a2d1ebd7be5ced828583.png",
"bitmapResolution": 2,
"rotationCenterX": 480,
"rotationCenterY": 360
}],
"currentCostumeIndex": 0,
"scratchX": 0,
"scratchY": 0,
"scale": 1.01,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"indexInLibrary": 1,
"visible": true,
"spriteInfo": {
}
},
{
"objName": "drag",
"scripts": [[5,
5,
[["whenGreenFlag"], ["setSizeTo:", 101], ["gotoX:y:", -413, -7], ["comeToFront"]]],
[10,
129,
[["whenIReceive", "slide"],
["doRepeat",
23,
[["gotoX:y:",
["+", ["xpos"], ["\/", ["-", 40, ["xpos"]], 7]],
-7]]]]]],
"sounds": [{
"soundName": "pop",
"soundID": 0,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
}],
"costumes": [{
"costumeName": "dotstickie3",
"baseLayerID": 2,
"baseLayerMD5": "e822121ae459cc14df1a5609abe4fd39.svg",
"bitmapResolution": 1,
"rotationCenterX": 296,
"rotationCenterY": 189
}],
"currentCostumeIndex": 0,
"scratchX": 26.928601934244593,
"scratchY": -7,
"scale": 1.01,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"indexInLibrary": 2,
"visible": true,
"spriteInfo": {
}
},
{
"objName": "clicktostart",
"scripts": [[5,
7,
[["whenGreenFlag"],
["hide"],
["wait:elapsed:from:", 1],
["comeToFront"],
["setGraphicEffect:to:", "ghost", 100],
["show"],
["doRepeat", 25, [["changeGraphicEffect:by:", "ghost", -4]]],
["doWaitUntil", ["mousePressed"]],
["doRepeat", 25, [["changeGraphicEffect:by:", "ghost", 4]]],
["hide"],
["broadcast:", "slide"]]]],
"sounds": [{
"soundName": "pop",
"soundID": 0,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
}],
"costumes": [{
"costumeName": "costume1",
"baseLayerID": 3,
"baseLayerMD5": "826c7f77077cfba8abddeae0229caf22.svg",
"bitmapResolution": 1,
"rotationCenterX": 139,
"rotationCenterY": -60
},
{
"costumeName": "costume2",
"baseLayerID": 4,
"baseLayerMD5": "a1c639c03c30e32b5baf48ea18621bc4.png",
"bitmapResolution": 2,
"rotationCenterX": 278,
"rotationCenterY": -132
}],
"currentCostumeIndex": 1,
"scratchX": 69,
"scratchY": -30,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"indexInLibrary": 3,
"visible": false,
"spriteInfo": {
}
},
{
"objName": "Sprite1",
"sounds": [{
"soundName": "pop",
"soundID": 0,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
}],
"costumes": [{
"costumeName": "costume1",
"baseLayerID": 5,
"baseLayerMD5": "7e82b18194f5cc47fba05a7ee5420172.svg",
"bitmapResolution": 1,
"rotationCenterX": 113,
"rotationCenterY": 86
}],
"currentCostumeIndex": 0,
"scratchX": -35,
"scratchY": 21,
"scale": 1,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"indexInLibrary": 4,
"visible": true,
"spriteInfo": {
}
}],
"info": {
"spriteCount": 4,
"userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/67.0.3396.99 Safari\/537.36",
"scriptCount": 4,
"projectID": "239379933",
"hasCloudData": true,
"swfVersion": "v461",
"videoOn": false,
"flashVersion": "MAC 30,0,0,154"
}
}
我查看了如何解析复杂的json,大多数答案都告诉我创建一个struct
并尝试尽可能地匹配数据,因此我创建了这个:
fileprivate struct Stage: Decodable {
struct Sound: Decodable {
var soundName: String;
var soundID: Int;
var md5: String;
var sampleCount: Int;
var rate: Int;
var format: String;
}
struct Costume: Decodable {
var costumeName: String;
var baseLayerID: Int;
var baseLayerMD5: String;
var bitmapResolution: Int;
var rotationCenterX: Int;
var rotationCenterY: Int;
}
struct Child: Decodable {
var objName: String;
var scripts: [[Any]];
var sounds: [Sound];
var costumes: [Costume];
var currentCostumeIndex: Int;
var scratchX: Int;
var scratchY: Int;
var scale: Float;
var direction: Int;
var rotationStyle: String;
var isDraggable: Bool;
var indexInLibrary: Int;
var visible: Bool;
var spriteInfo: SpriteInfo;
}
struct SpriteInfo: Decodable {
}
struct Info: Decodable {
var spriteCount: Int;
var userAgent: String;
var scriptCount: Int;
var projectID: String;
var hasCloudData: Bool;
var swfVersion: String;
var videoOn: Bool;
var flashVersion: String;
}
var objName: String;
var sounds: [Sound];
var costumes: [Costume];
var currentCostumeIndex: Int;
var penLayerMD5: String;
var penLayerID: Int;
var tempoBPM: Int;
var videoAlpha: Float;
var children: [Child];
var info: Info;
}
我编译我的swift时,它说“错误:类型'Stage.Child'不符合协议'Decodable'”
我不知道该如何解决。
任何帮助将不胜感激。
答案 0 :(得分:1)
与其他陈述一样,问题在于如何解码scripts
属性。 JSON的其余部分非常传统,因此我将它们粘贴到quicktype.io并得到以下结构:
struct Stage: Decodable {
let objName: String
let sounds: [Sound]
let costumes: [Costume]
let currentCostumeIndex: Int
let penLayerMD5: String
let penLayerID, tempoBPM: Int
let videoAlpha: Double
let children: [Child]
let info: Info
}
struct Child: Decodable {
let objName: String
let scripts: [Script]? // We will write a custom decoder for `Script`
let sounds: [Sound]
let costumes: [Costume]
let currentCostumeIndex: Int
let scratchX: Double
let scratchY: Int
let scale: Double
let direction: Int
let rotationStyle: String
let isDraggable: Bool
let indexInLibrary: Int
let visible: Bool
let spriteInfo: SpriteInfo
}
struct Costume: Decodable {
let costumeName: String
let baseLayerID: Int
let baseLayerMD5: String
let bitmapResolution, rotationCenterX, rotationCenterY: Int
}
struct Sound: Decodable {
let soundName: String
let soundID: Int
let md5: String
let sampleCount, rate: Int
let format: String
}
struct SpriteInfo: Decodable { }
struct Info: Decodable {
let spriteCount: Int
let userAgent: String
let scriptCount: Int
let projectID: String
let hasCloudData: Bool
let swfVersion: String
let videoOn: Bool
let flashVersion: String
}
Script
本节中对
CustomDebugStringConvertible
的所有符合都是可选的。您无需包括它们即可使代码起作用。它们仅用于调试。
Script
对象下面的3个实例代表Script
的有效数组:
// Example 1
[
[5, 5, [["whenGreenFlag"], ["setSizeTo:", 101], ["gotoX:y:", 0, 0]]]
]
// Example 2
[
[5, 7, [["whenGreenFlag"],["hide"],["wait:elapsed:from:",1],["comeToFront"],["setGraphicEffect:to:","ghost",100],["show"],["doRepeat",25,[["changeGraphicEffect:by:","ghost",-4]]],["doWaitUntil",["mousePressed"]],["doRepeat",25,[["changeGraphicEffect:by:","ghost",4]]],["hide"],["broadcast:","slide"]]]
]
// Example 3:
[
[5, 5, [["whenGreenFlag"], ["setSizeTo:", 101], ["gotoX:y:", -413, -7], ["comeToFront"]]],
[10, 129, [["whenIReceive", "slide"], ["doRepeat", 23, [["gotoX:y:", ["+", ["xpos"], ["/", ["-", 40, ["xpos"]], 7]], -7]]]]]
]
从这些示例中,我们可以看到Script
以2个数字开头,然后是看似命令的数组,因此我们可以像这样解码Script
:
struct Script: Decodable, CustomDebugStringConvertible {
var firstNumber: Int
var secondNumber: Int
var commands: [Command]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.firstNumber = try container.decode(Int.self)
self.secondNumber = try container.decode(Int.self)
self.commands = try container.decode([Command].self)
}
var debugDescription: String {
return "Script: \(firstNumber), \(secondNumber), \(commands.debugDescription)"
}
}
Command
对象继续使用Command
,例如:
["whenGreenFlag"]
["setSizeTo:", 101]
["whenIReceive", "slide"]
["gotoX:y:", 0, 0]
["doRepeat",25,[["changeGraphicEffect:by:","ghost",4]]]
因此,每个Command
以一个字符串开头,后跟0个或多个参数。每个Argument
可以是整数,字符串或另一个Command
:
struct Command: Decodable, CustomDebugStringConvertible {
var name: String
var arguments = [CommandArgument]()
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.name = try container.decode(String.self)
while !container.isAtEnd {
let argument = try container.decode(CommandArgument.self)
self.arguments.append(argument)
}
}
var debugDescription: String {
return "(\(name) \(arguments.debugDescription))"
}
}
CommandArgument
对象 CommandArgument
s的示例:
101
"slide"
["doRepeat", 23, [["gotoX:y:", ["+", ["xpos"], ["/", ["-", 40, ["xpos"]], 7]], -7]]]
每个CommandArgument
可以是整数,字符串或其他命令。我们将使用具有关联值的枚举来表示它:
enum CommandArgument: Decodable, CustomDebugStringConvertible {
case integer(value: Int)
case string(value: String)
case command(value: Command)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let intValue = try? container.decode(Int.self) {
self = .integer(value: intValue)
} else if let stringValue = try? container.decode(String.self) {
self = .string(value: stringValue)
} else if let commandValue = try? container.decode(Command.self) {
self = .command(value: commandValue)
} else if let commandArray = try? container.decode([Command].self) {
self = .command(value: commandArray.first!)
} else {
throw NSError(domain: NSCocoaErrorDomain, code: 1, userInfo: [NSLocalizedDescriptionKey: "Unrecognized argument type"])
}
}
var debugDescription: String {
switch self {
case .integer(let intValue):
return "\(intValue)"
case .string(let stringValue):
return stringValue
case .command(let commandValue):
return commandValue.debugDescription
}
}
}
现在您已经完成了所有编码,这就是解码整个JSON的方法:
let stage = try JSONDecoder().decode(Stage.self, from: json)