我正在尝试解析这个 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’不符合协议(protocol)‘Decodable’”
我不知道该如何解决这个问题。
如有任何帮助,我们将不胜感激。
最佳答案
简单的部分
像其他人所说的那样,问题在于如何解码 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
}
解码脚本
All conformances to
CustomDebugStringConvertible
in this section are optional. You do not have to include them for the code to work. They are to assist debugging only.
脚本
对象
下面的 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)"
}
}
命令
对象
继续命令
,一些例子是:
["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
示例:
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)
关于json - 解析真的很复杂的json? swift ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51921713/