从 Swift 中自定义类型的多个属性构建 json 数组
Build a json array from multiple properties of a custom type in Swift
在被指向使用 Codable 格式化 Swift 中的 json 之后,我试图将多个结果添加到 json 数组。不幸的是,我正在努力研究如何为包含多个属性的一组结果构建这个 json 数组。
这是我需要的 json 数组的示例,用于两个 'sets' 结果(N.B。集合的数量可能因记录的数据而异):
[{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"orientation_forward","value":"2"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"start_forward","value":"3.45"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"finish_forward","value":"5.29"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"minimum_forward","value":"7.81"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"maximum_forward","value":"9.20"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"range_forward","value":"2.39"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"orientation_forward","value":"1"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"start_forward","value":"1.89"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"finish_forward","value":"4.20"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"minimum_forward","value":"8.26"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"maximum_forward","value":"46.82"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"range_forward","value":"2.98"}]
下面是我的 Swift 代码。结果作为自定义类型数组 ORKRangeOfMotionResult
中的一个参数到达(来自 ResearchKit
,每组结果给我一个值 orientation
、start
、finish
、minumum
、maximum
和 range
属性)。每组 ORKRangeOfMotionResult
还附带一个标识符 (variableIdentifier
),作为另一个结果提取,我可以使用它来确保我知道哪个集合是哪个。
我目前正在使用循环为每个 ORKRangeOfMotionResult
属性 提取单独的值。在此之后,使用 if
语句中的 variableIdentifier
将一个结果添加到 json 数组 [items]
相对容易(如下面的 start
) 但我不知道如何将一组结果中的所有属性添加到数组中。我猜是一个循环,但还没有弄清楚是怎么回事。
func buildJsonArrayOfResults(_ rangeOfMotionResults: [ORKRangeOfMotionResult], withIdentifier variableIdentifiers: [String]) {
let record = getIDNumber().stringValue
let repeat_instance = getRepeatInstance().stringValue
let repeat_instrument = "range_of_motion_result"
let event = getEventName()
var fieldName: String?
var identifier: String?
var stringData: String?
var value: String?
var items: [Item] = []
struct Item: Codable {
let record: String
let fieldName: String
let repeatInstance: String
let repeatInstrument: String
let value: String
let event: String
enum CodingKeys: String, CodingKey {
case record
case fieldName = "field_name"
case repeatInstance = "redcap_repeat_instance"
case repeatInstrument = "redcap_repeat_instrument"
case value
case event = "redcap_event_name"
}
}
var setNumber: Int = 0
while setNumber < rangeOfMotionResults.count {
setNumber += 1
// obtain results for each set of movements as a string
let orientation = String(rangeOfMotionResults[setNumber - 1].orientation)
let start = String(rangeOfMotionResults[setNumber - 1].start)
let finish = String(rangeOfMotionResults[setNumber - 1].finish)
let minimum = String(rangeOfMotionResults[setNumber - 1].minimum)
let maximum = String(rangeOfMotionResults[setNumber - 1].maximum)
let range = String(rangeOfMotionResults[setNumber - 1].range)
// assign one value and its redcap field name
identifier = variableIdentifiers[setNumber - 1]
if identifier!.contains("forward") {
fieldName = "start_forward"
value = start
} else if identifier!.contains("backward") {
fieldName = "start_backward"
value = start
} else if identifier!.contains("left.bending") {
fieldName = "start_left_bending"
value = start
} else if identifier!.contains("right.bending") {
fieldName = "start_right_bending"
value = start
} else if identifier!.contains("left.rotation") {
fieldName = "start_left_rotation"
value = start
} else if identifier!.contains("right.rotation") {
fieldName = "start_left_rotation"
value = start
}
let item = Item(record: record, fieldName: fieldName!, repeatInstance: String(repeat_instance), repeatInstrument: repeat_instrument, value: value!, event: event)
items.append(item)
}
do {
let data = try JSONEncoder().encode(items)
stringData = String(data: data, encoding: .utf8)
print(stringData!)
} catch {
print(error)
}
// now do something with stringData
}
所有的帮助都被慷慨地接受了。提前谢谢你。
根据给定的场景,您只需要这些属性
func buildJsonArrayOfResults(_ rangeOfMotionResults: [ORKRangeOfMotionResult], withIdentifier variableIdentifiers: [String]) {
let record = getIDNumber().stringValue
let repeatInstance = getRepeatInstance().stringValue
let repeatInstrument = "range_of_motion_result"
let event = getEventName()
var items: [Item] = []
struct Item: Codable { ... }
并且由于标识符的顺序与结果数组的顺序相匹配,推荐的枚举数组索引和元素的方法是enumerated
for (index, result) in rangeOfMotionResults.enumerated() {
let identifier = variableIdentifiers[index]
并且 finish
、minimum
、maximum
和 range
似乎未被使用
// obtain results for each set of movements as a string
let orientation = String(result.orientation)
let value = String(result.start)
switch
表达式 比 if - else
链
更快
let fieldName : String
switch identifier {
case let id where id.contains("forward"): fieldName = "start_forward"
case let id where id.contains("backward"): fieldName = "start_backward"
case let id where id.contains("left.bending"): fieldName = "start_left_bending"
case let id where id.contains("right.bending"): fieldName = "start_right_bending"
case let id where id.contains("left.rotation"): fieldName = "start_left_rotation"
case let id where id.contains("right.rotation"): fieldName = "start_left_rotation"
default: fieldName = "unknown"
}
let item = Item(record: record, fieldName: fieldName, repeatInstance: repeatInstance, repeatInstrument: repeatInstrument, value: value, event: event)
items.append(item)
}
do {
let data = try JSONEncoder().encode(items)
let stringData = String(data: data, encoding: .utf8)!
print(stringData)
// now do something with stringData
} catch {
print(error)
}
}
我还建议在 for
循环之前插入这一行
assert(rangeOfMotionResults.count == variableIdentifiers.count, "The size of rangeOfMotionResults and variableIdentifiers must be equal")
它检查两个数组的大小是否相等,在 Release Build 中忽略该表达式。
感谢@vadian 的回答,我能够通过添加一个新的 struct
来完全解决这个问题,其中包含一个 key-value pair
和一个标识符(以及 array
这些 'trios') 然后添加第二个 for-in loop
。我确信我的代码可以进一步优化(这会很有趣),但我很高兴(也很感激)现在正在按照我想要的方式构建 json 数组。这是我的最终解决方案:
func buildJsonArrayOfResults(_ rangeOfMotionResults: [ORKRangeOfMotionResult], withIdentifier variableIdentifiers: [String]) {
let record = getIDNumber().stringValue
let repeatInstance = getRepeatInstance().stringValue
let repeatInstrument = "range_of_motion_result"
let event = getEventName()
var fieldName: String?
var identifier: String?
var stringData: String?
var value: String?
var items: [Item] = []
struct Item: Codable {
let record: String
let fieldName: String
let repeatInstance: String
let repeatInstrument: String
let value: String
let event: String
enum CodingKeys: String, CodingKey {
case record
case fieldName = "field_name"
case repeatInstance = "redcap_repeat_instance"
case repeatInstrument = "redcap_repeat_instrument"
case value
case event = "redcap_event_name"
}
}
struct Trio {
let key: String
let value: String
let identifier: String
}
var trios = [Trio]()
assert(rangeOfMotionResults.count == variableIdentifiers.count, "The size of rangeOfMotionResults and variableIdentifiers must be equal")
for (index, result) in rangeOfMotionResults.enumerated() {
identifier = variableIdentifiers[index]
// add key-value pairs with their identifiers to array
let orientation = String(result.orientation)
trios.append(Trio(key: "orientation", value: orientation, identifier: identifier!))
let start = String(result.start)
trios.append(Trio(key: "start", value: start, identifier: identifier!))
let finish = String(result.finish)
trios.append(Trio(key: "finish", value: finish, identifier: identifier!))
let minimum = String(result.minimum)
trios.append(Trio(key: "minimum", value: minimum, identifier: identifier!))
let maximum = String(result.maximum)
trios.append(Trio(key: "maximum", value: maximum, identifier: identifier!))
let range = String(result.range)
trios.append(Trio(key: "range", value: range, identifier: identifier!))
}
for (_, result) in trios.enumerated() {
let ident = result.identifier
value = result.value
let fieldName: String
switch ident {
case let id where id.contains("forward"):
fieldName = result.key + "_forward"
case let id where id.contains("backward"):
fieldName = result.key + "_backward"
case let id where id.contains("left.bending"):
fieldName = result.key + "_left_bending"
case let id where id.contains("right.bending"):
fieldName = result.key + "_right_bending"
case let id where id.contains("left.rotation"):
fieldName = result.key + "_left_rotation"
case let id where id.contains("right.rotation"):
fieldName = result.key + "_left_rotation"
default:
fieldName = result.key + "_unknown"
}
let item = Item(record: record, fieldName: fieldName, repeatInstance: String(repeatInstance), repeatInstrument: repeatInstrument, value: value!, event: event)
items.append(item)
}
do {
let data = try JSONEncoder().encode(items)
stringData = String(data: data, encoding: .utf8)
print(stringData!)
// now do something with stringData
} catch {
print(error)
}
}
在被指向使用 Codable 格式化 Swift 中的 json 之后,我试图将多个结果添加到 json 数组。不幸的是,我正在努力研究如何为包含多个属性的一组结果构建这个 json 数组。
这是我需要的 json 数组的示例,用于两个 'sets' 结果(N.B。集合的数量可能因记录的数据而异):
[{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"orientation_forward","value":"2"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"start_forward","value":"3.45"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"finish_forward","value":"5.29"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"minimum_forward","value":"7.81"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"maximum_forward","value":"9.20"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"1","field_name":"range_forward","value":"2.39"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"orientation_forward","value":"1"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"start_forward","value":"1.89"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"finish_forward","value":"4.20"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"minimum_forward","value":"8.26"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"maximum_forward","value":"46.82"},
{"record":"10","redcap_repeat_instrument":"range_of_motion_result","redcap_repeat_instance":"2","field_name":"range_forward","value":"2.98"}]
下面是我的 Swift 代码。结果作为自定义类型数组 ORKRangeOfMotionResult
中的一个参数到达(来自 ResearchKit
,每组结果给我一个值 orientation
、start
、finish
、minumum
、maximum
和 range
属性)。每组 ORKRangeOfMotionResult
还附带一个标识符 (variableIdentifier
),作为另一个结果提取,我可以使用它来确保我知道哪个集合是哪个。
我目前正在使用循环为每个 ORKRangeOfMotionResult
属性 提取单独的值。在此之后,使用 if
语句中的 variableIdentifier
将一个结果添加到 json 数组 [items]
相对容易(如下面的 start
) 但我不知道如何将一组结果中的所有属性添加到数组中。我猜是一个循环,但还没有弄清楚是怎么回事。
func buildJsonArrayOfResults(_ rangeOfMotionResults: [ORKRangeOfMotionResult], withIdentifier variableIdentifiers: [String]) {
let record = getIDNumber().stringValue
let repeat_instance = getRepeatInstance().stringValue
let repeat_instrument = "range_of_motion_result"
let event = getEventName()
var fieldName: String?
var identifier: String?
var stringData: String?
var value: String?
var items: [Item] = []
struct Item: Codable {
let record: String
let fieldName: String
let repeatInstance: String
let repeatInstrument: String
let value: String
let event: String
enum CodingKeys: String, CodingKey {
case record
case fieldName = "field_name"
case repeatInstance = "redcap_repeat_instance"
case repeatInstrument = "redcap_repeat_instrument"
case value
case event = "redcap_event_name"
}
}
var setNumber: Int = 0
while setNumber < rangeOfMotionResults.count {
setNumber += 1
// obtain results for each set of movements as a string
let orientation = String(rangeOfMotionResults[setNumber - 1].orientation)
let start = String(rangeOfMotionResults[setNumber - 1].start)
let finish = String(rangeOfMotionResults[setNumber - 1].finish)
let minimum = String(rangeOfMotionResults[setNumber - 1].minimum)
let maximum = String(rangeOfMotionResults[setNumber - 1].maximum)
let range = String(rangeOfMotionResults[setNumber - 1].range)
// assign one value and its redcap field name
identifier = variableIdentifiers[setNumber - 1]
if identifier!.contains("forward") {
fieldName = "start_forward"
value = start
} else if identifier!.contains("backward") {
fieldName = "start_backward"
value = start
} else if identifier!.contains("left.bending") {
fieldName = "start_left_bending"
value = start
} else if identifier!.contains("right.bending") {
fieldName = "start_right_bending"
value = start
} else if identifier!.contains("left.rotation") {
fieldName = "start_left_rotation"
value = start
} else if identifier!.contains("right.rotation") {
fieldName = "start_left_rotation"
value = start
}
let item = Item(record: record, fieldName: fieldName!, repeatInstance: String(repeat_instance), repeatInstrument: repeat_instrument, value: value!, event: event)
items.append(item)
}
do {
let data = try JSONEncoder().encode(items)
stringData = String(data: data, encoding: .utf8)
print(stringData!)
} catch {
print(error)
}
// now do something with stringData
}
所有的帮助都被慷慨地接受了。提前谢谢你。
根据给定的场景,您只需要这些属性
func buildJsonArrayOfResults(_ rangeOfMotionResults: [ORKRangeOfMotionResult], withIdentifier variableIdentifiers: [String]) {
let record = getIDNumber().stringValue
let repeatInstance = getRepeatInstance().stringValue
let repeatInstrument = "range_of_motion_result"
let event = getEventName()
var items: [Item] = []
struct Item: Codable { ... }
并且由于标识符的顺序与结果数组的顺序相匹配,推荐的枚举数组索引和元素的方法是enumerated
for (index, result) in rangeOfMotionResults.enumerated() {
let identifier = variableIdentifiers[index]
并且 finish
、minimum
、maximum
和 range
似乎未被使用
// obtain results for each set of movements as a string
let orientation = String(result.orientation)
let value = String(result.start)
switch
表达式 比 if - else
链
let fieldName : String
switch identifier {
case let id where id.contains("forward"): fieldName = "start_forward"
case let id where id.contains("backward"): fieldName = "start_backward"
case let id where id.contains("left.bending"): fieldName = "start_left_bending"
case let id where id.contains("right.bending"): fieldName = "start_right_bending"
case let id where id.contains("left.rotation"): fieldName = "start_left_rotation"
case let id where id.contains("right.rotation"): fieldName = "start_left_rotation"
default: fieldName = "unknown"
}
let item = Item(record: record, fieldName: fieldName, repeatInstance: repeatInstance, repeatInstrument: repeatInstrument, value: value, event: event)
items.append(item)
}
do {
let data = try JSONEncoder().encode(items)
let stringData = String(data: data, encoding: .utf8)!
print(stringData)
// now do something with stringData
} catch {
print(error)
}
}
我还建议在 for
循环之前插入这一行
assert(rangeOfMotionResults.count == variableIdentifiers.count, "The size of rangeOfMotionResults and variableIdentifiers must be equal")
它检查两个数组的大小是否相等,在 Release Build 中忽略该表达式。
感谢@vadian 的回答,我能够通过添加一个新的 struct
来完全解决这个问题,其中包含一个 key-value pair
和一个标识符(以及 array
这些 'trios') 然后添加第二个 for-in loop
。我确信我的代码可以进一步优化(这会很有趣),但我很高兴(也很感激)现在正在按照我想要的方式构建 json 数组。这是我的最终解决方案:
func buildJsonArrayOfResults(_ rangeOfMotionResults: [ORKRangeOfMotionResult], withIdentifier variableIdentifiers: [String]) {
let record = getIDNumber().stringValue
let repeatInstance = getRepeatInstance().stringValue
let repeatInstrument = "range_of_motion_result"
let event = getEventName()
var fieldName: String?
var identifier: String?
var stringData: String?
var value: String?
var items: [Item] = []
struct Item: Codable {
let record: String
let fieldName: String
let repeatInstance: String
let repeatInstrument: String
let value: String
let event: String
enum CodingKeys: String, CodingKey {
case record
case fieldName = "field_name"
case repeatInstance = "redcap_repeat_instance"
case repeatInstrument = "redcap_repeat_instrument"
case value
case event = "redcap_event_name"
}
}
struct Trio {
let key: String
let value: String
let identifier: String
}
var trios = [Trio]()
assert(rangeOfMotionResults.count == variableIdentifiers.count, "The size of rangeOfMotionResults and variableIdentifiers must be equal")
for (index, result) in rangeOfMotionResults.enumerated() {
identifier = variableIdentifiers[index]
// add key-value pairs with their identifiers to array
let orientation = String(result.orientation)
trios.append(Trio(key: "orientation", value: orientation, identifier: identifier!))
let start = String(result.start)
trios.append(Trio(key: "start", value: start, identifier: identifier!))
let finish = String(result.finish)
trios.append(Trio(key: "finish", value: finish, identifier: identifier!))
let minimum = String(result.minimum)
trios.append(Trio(key: "minimum", value: minimum, identifier: identifier!))
let maximum = String(result.maximum)
trios.append(Trio(key: "maximum", value: maximum, identifier: identifier!))
let range = String(result.range)
trios.append(Trio(key: "range", value: range, identifier: identifier!))
}
for (_, result) in trios.enumerated() {
let ident = result.identifier
value = result.value
let fieldName: String
switch ident {
case let id where id.contains("forward"):
fieldName = result.key + "_forward"
case let id where id.contains("backward"):
fieldName = result.key + "_backward"
case let id where id.contains("left.bending"):
fieldName = result.key + "_left_bending"
case let id where id.contains("right.bending"):
fieldName = result.key + "_right_bending"
case let id where id.contains("left.rotation"):
fieldName = result.key + "_left_rotation"
case let id where id.contains("right.rotation"):
fieldName = result.key + "_left_rotation"
default:
fieldName = result.key + "_unknown"
}
let item = Item(record: record, fieldName: fieldName, repeatInstance: String(repeatInstance), repeatInstrument: repeatInstrument, value: value!, event: event)
items.append(item)
}
do {
let data = try JSONEncoder().encode(items)
stringData = String(data: data, encoding: .utf8)
print(stringData!)
// now do something with stringData
} catch {
print(error)
}
}