json - 解析真的很复杂的json? swift

标签 json swift

我正在尝试解析这个 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/

相关文章:

ios - Swift 过滤对象数组中包含的相同位置坐标

java - 我如何使用 i18n ? (吉普斯特)

java - 使用 gson 创建复杂的 json 对象

json - Swift 4 将数据从 json 保存到数组以在 TableView 中显示

ios - Sprite Kit 中的弹跳标签

SwiftUI - 半模态?

ios - 在 Swift 中不使用 didSelectRowAtIndexPath 在 DetailView 中引用特定于单元格的 JSON 键

mysql - 哪些数据库即服务提供商(最好是 Mongo 或 MySQL 数据源)提供用于以 JSON 形式检索数据的 REST API?

python - Python中将Json Dict对象转换为DataFrame

ios - UICollectionView 未在屏幕上加载