【问题标题】:convert to JSON转换为 JSON
【发布时间】:2021-04-29 00:44:29
【问题描述】:

我正在通过访问苹果的 healthkit 中的健康数据来开发一个健康应用程序。我可以访问血压和 BMI 数据。

我希望将此数据以 JSON 格式发送回我的 JS 文件。

要求的格式是这样的

{   "items" : [
    {
      "endDate" : "2020-01-25",
      "BloodPressure" : "122/65",
      "startDate" : "2020-01-25"
    },
    {
      "endDate" : "2020-01-25",
      "BMI" : "24.6",
      "startDate" : "2020-01-25"
    }   ] }

我的 BP 和 BMI 查询是:

func getBloodPressure(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
    var value : Any = ""
    guard let type = HKQuantityType.correlationType(forIdentifier: HKCorrelationTypeIdentifier.bloodPressure),
                let systolicType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureSystolic),
                let diastolicType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureDiastolic) else {

                    return
            }
         let now = Date()
         let startDate = Calendar.current.startOfDay(for: now)
         let endDate = now
         let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)

            let sampleQuery = HKSampleQuery(sampleType: type, predicate: predicate, limit: 0, sortDescriptors: nil) { (sampleQuery, results, error) in
                if let dataList = results as? [HKCorrelation] {
                    for data in dataList
                    {
                        if let data1 = data.objects(for: systolicType).first as? HKQuantitySample,
                            let data2 = data.objects(for: diastolicType).first as? HKQuantitySample {

                            let value1 = data1.quantity.doubleValue(for: HKUnit.millimeterOfMercury())
                            let value2 = data2.quantity.doubleValue(for: HKUnit.millimeterOfMercury())
                            value = "\(value1) / \(value2)"
                            resolve(value)
                        }}}}
                   healthStore.execute(sampleQuery)
      }

 @objc
  func getBMI(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
    let bodyMassType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMassIndex)
    let query = HKSampleQuery(sampleType: bodyMassType!, predicate: nil, limit: 1, sortDescriptors: nil) { (query, results, error) in
                if let result = results?.first as? HKQuantitySample {
                  let bodyMassIndex = result.quantity.doubleValue(for: HKUnit.count())
                    print("BMI in xcode",bodyMassIndex, result.endDate)
                  resolve(bodyMassIndex)
                    return
                }}
       healthStore.execute(query)
      }

现在我创建了 2 个结构:

struct BloodPressureItem: Codable {
        let endDate: String?
        let BloodPressure: String?
        let startDate: String?
    }

struct BodyMassIndexItem: Codable {
        let endDate: String?
        let BMI: String?
        let startDate: String?
    }

接下来我将我的数据附加到对象中。

let jsonData = BloodPressureItem.init(endDate: end, Bloodpressure: String(value), startDate: start)
let jsonData = BodyMassIndexItem.init(endDate: end, BMI: String(bodyMassIndex), startDate: start)

我得到的结果是

BloodPressureItem(endDate: Optional("2021-01-25 07:43:27 +0000"), BloodPressure: Optional("122.0/65.0"), startDate: Optional("2021-01-24 07:43:27 +0000"))

BodyMassIndexItem(endDate: Optional("2021-01-25 07:43:27 +0000"), BMI: Optional("24.6"), startDate: Optional("2021-01-24 07:43:27 +0000"))

接下来如何将其转换为我需要的格式?

更新:

{   "items" : [
    “BloodPressure:” {
      "endDate" : "2020-01-25",
      “Value” : "122/65",
      "startDate" : "2020-01-25"
    },
   “BMI:” {
      "endDate" : "2020-01-25",
      “Value” : "24.6",
      "startDate" : "2020-01-25"
    }   ] }

【问题讨论】:

  • Encodable 对异构数组进行编码是相当费力的。我更喜欢JSONSerialization
  • 我会使用枚举,并根据键使用一个或另一个。枚举很棒!

标签: ios json swift


【解决方案1】:

我喜欢枚举,而且我绝对会在这里使用枚举来处理这些多态类型。这样您就可以使您的数据保持非可选状态,并避免在所有地方不必要地使用???

type 与数据一起存储以轻松解码也是一个好主意

/// An enum that represents the polymorphic json object that will be stored/sent
enum HealthRecord: Encodable {
    case bmi(BodyMassIndexItem)
    case bloodPressure(BloodPressureItem)
    
    /// The type will be stored by the encoder to make easier decoding.
    private var type: HealthRecordType {
        switch self {
        case .bloodPressure: return .bloodPressure
        case .bmi: return .bmi
        }
    }
    
    /// Coding keys are used by the encoder/decoder
    private enum CodingKeys: String, CodingKey {
        case type
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        // Encode `type` for easier future decoding
        try container.encode(type, forKey: .type)
        
        // Encode the polymorphic type
        switch self {
        case .bmi(let bodyMassIndex):
            // Encode the contents of the BMI
            try bodyMassIndex.encode(to: encoder)
        case .bloodPressure(let bloodPressure):
            // Encode the contents of blood pressure
            try bloodPressure.encode(to: encoder)
        }
    }
}

/// A private enum used to tell us the type of health record this is (so we don't have to look at keys). This value will tell us how to decode the object in the future.
private enum HealthRecordType: String, Encodable {
    case bmi
    case bloodPressure
}

struct BodyMassIndexItem: Encodable {
    let endDate: String?
    let BloodPressure: String?
    let startDate: String?
}

struct BloodPressureItem: Encodable {
    let endDate: String?
    let BloodPressure: String?
    let startDate: String?
}

多次提及:

  1. 您可以使用 dateEncodingStrategy(或其他机制)对日期进行编码
  2. 如果你知道它们永远不是可选的,你就不应该让它们成为可选的
  3. 您可以使用 Date() 获取当前日期
  4. 我不会详细介绍,但我认为从您的日期对象中删除时间是有风险的。如果您不知道自己在处理日期,请考虑使用 RFC3339ISO8601DateFormatter 日期格式化程序:https://developer.apple.com/documentation/foundation/dateformatter

【讨论】:

    【解决方案2】:

    创建包含其他两个的第三个结构

    struct HealthData: Codable {
        let bloodPressure: BloodPressureItem
        let bmi: BodyMassIndexItem
    }
    

    然后编码这个结构的一个实例

    let bloodPressure = BloodPressureItem.init(endDate: end, Bloodpressure: String(value), startDate: start)
    let bmi = BodyMassIndexItem(endDate: end, BMI: String(bodyMassIndex), startDate: start)
    
    let healthData = HealthData(bloodPressure: bloodPressureItem, bmi: bmiItem)
    
    do {
        let data = try JSONEncoder().encode(healthData)
    } catch { 
         //error handling
    }
    

    我假设你想发送一个,否则你可以简单地将属性更改为数组,

    struct HealthData: Codable {
        let bloodPressureValues: [BloodPressureItem]
        let bmiValues: [BodyMassIndexItem]
    }
    

    在您的代码中,您已将所有内容都设置为可选的字符串类型,我建议不要使用可选的,而是使用原始类型,如

    struct BloodPressureItem: Codable {
        let endDate: Date
        let BloodPressure: Double
        let startDate: Date
    }
    

    另一种可能的解决方案是对所有类型的数据使用单个自定义类型

    enum HealthDataType: String, Codable {
        case bloodPressure
        case bmi
    }
    struct HealtDataItem: Codable {
        let endDate: Date
        let value: Double
        let startDate: Date
        let type: HealthDataType
    }
    

    然后将所有对象添加到数组中并对数组进行编码

    let bloodPressureItem = HealtDataItem(endDate: end, value: bloodPressureValue, startDate: start, type: .bloodPressure)
    let bmiItem = HealtDataItem(endDate: end, value: bmiValue, startDate: start, type: .bmi)
    
    let healthData = [bloodPressureItem, bmiItem]
    
    do {
        let data = try JSONEncoder().encode(healthData)
    } catch { 
         //error handling
    }
    

    【讨论】:

    • 枚举方式是一种极好的方法。谢谢伙计,它起作用了,它打印了一组对象。最后一个查询如果我想将 HealthDataType 移到对象之外(请参阅我更新的问题的最后一部分)我是否必须创建一个新对象并将 HealthDataItem 附加到它?
    • 您可以使用字典而不是数组,但请尊重您一次只能问一个问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-20
    • 2015-08-23
    • 1970-01-01
    • 1970-01-01
    • 2017-01-06
    • 2013-05-09
    相关资源
    最近更新 更多