Быстрое преобразование JSON UnionValue в объект и цикл

#ios #json #swift

Вопрос:

Получен ответ Json

 {
"data": [
    {
        "key": "email",
        "value": "qfmppddybsfeiiiazh@mrvpt.com"
    },
    {
        "key": "name",
        "value": "Tg baa"
    },
    {
        "key": "dob",
        "value": "1999-06-06"
    },
    {
        "key": "nationality",
        "value": "UK"
    },
    {
        "key": "address",
        "value": [
            {
                "key": "addr1",
                "value": "Bundle road, near church 460102"
            },
            {
                "key": "postalCode",
                "value": "46HG02"
            },
            {
                "key": "city",
                "value": "London"
            }
        ]
    }
],
"Greeting": "Birthday",
"Service": "mydelivery"
 

}

Сгенерированная модель с помощью онлайн-инструмента

     // MARK: - Welcome
public struct Welcome: Codable {
    public let data: [Datum]
    public let Greeting, Service: String
}

// MARK: - Datum
public struct Datum: Codable {
    public let key: String
    public let value: ValueUnion
}

public enum ValueUnion: Codable {
    case string(String)
    case valueElementArray([ValueElement])

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode([ValueElement].self) {
            self = .valueElementArray(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(ValueUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ValueUnion"))
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let x):
            try container.encode(x)
        case .valueElementArray(let x):
            try container.encode(x)
        }
    }
}

// MARK: - ValueElement
public struct ValueElement: Codable {
    let key, value: String
}
 

Попробуйте получить доступ к массиву адресов в модели. но всегда терпит неудачу

 print("onboardingStatus: (result?.Greeting ?? "")")
        print("idService: (result?.Service ?? "")")
        guard let userData = result?.data else { return }
        for item in userData {
            if item.key == "address" {
                guard let address_info = item.value as? [ValueElement] else { return }
                
                for ad in address_info {
                    print(ad.key)
                    print(ad.value)
                }
            }
            print(item.key)
            print(item.value)
            
        }
 

Я хочу просмотреть ключ и значения адреса. но я получаю ошибку «Для цикла ввода требуется, чтобы» Объединение значений «соответствовало» Последовательности»». пожалуйста, помогите.

Комментарии:

1. Я скопировал/вставил ваш код, и я получил «Приведение от» Объединения значений «к несвязанному типу» [Элемент значения] «всегда не удается», а не вашу ошибку…

2. да, я хочу привести к [элементу ValueElement], чтобы я мог выполнить цикл.

3. вы должны преобразовать свой ответ JSON в объект модели следующим образом :- пусть decoder = JSONDecoder() сделает { пусть люди = попробуют декодер.декодировать(имя модели.само, от: результат.значение) печать(люди) } поймать { печать(ошибка.локализованное описание) }

4. Вы также можете удалить код рядом со структурой …. начиная с перечисления и до конца

Ответ №1:

Ошибка компилятора, которую вы должны получить на:

 guard let address_info = item.value as? [ValueElement] else { return }
 

является

 Cast from 'ValueUnion' to unrelated type '[ValueElement]'
 

Не «Для цикла» требуется, чтобы «Объединение значений «соответствовало»Последовательности»».

Ошибка явная, address_info это объединение значений, а не [ValueElement] . В вашем конкретном случае это почти так, но поскольку это а ValueUnion , это может быть а String вместо этого. В конце концов, ValueUnion это связанное перечисление значений для обработки строки и массива ValueElement .

Быстрое «исправление» было бы:

 if case let .valueElementArray(address_info) = item.value {
    for ad in address_info {
        print(ad.key)
        print(ad.value)
    }
}
 

Но это может быть слишком продвинуто (и бесполезно, если вы этого не понимаете).

 switch item.value {
    case .string(let aString):
        print("Found a string: (aString)")
    case .valueElementArray(let elements):
        for ad in elements {
            print(ad.key)
            print(ad.value)
        }
}
 

Теперь ваш JSON странный, и в конце концов вам может понадобиться модель, поэтому либо сделайте «сопоставление» между вашей текущей моделью в эту, либо с некоторой работой (возможно, слишком много работы для результата), декодируйте непосредственно в эту модель.

 struct UserOrSomething {
    let greeting: String
    let service: String
    let infos: Infos
}
struct Infos {
    let name: String
    let dob: String
    let email: String
    let nationality: String
    let address: Address
}

struct Address {
    let addr1: String
    let addr2: String
    let postalCode: String
    let city: String
    let state: String
    let region: String
}
 

Комментарии:

1. использование пункта включения работает . Спасибо вам за поддержку.

Ответ №2:

Ваше объединение значений может быть одним из двух различных значений, либо одной строкой, либо массивом пар ключ/значение. Вы говорите, что «хотите привести к [элементу значения]», чтобы вы могли перебирать их, но что, если объединение значений не содержит массива элементов значения?

Сначала вы должны выяснить, существует ли массив вообще в объединении значений…

 func example(valueUnion: ValueUnion) {
    if case let .valueElementArray(array) = valueUnion {
        for each in array {
            print("valueElement - key:", each.key, " value:", each.value)
        }
    }
}