如何从JSON文件中一次只解析一种语言?

iibxawm4  于 2023-04-08  发布在  其他
关注(0)|答案(2)|浏览(138)

我有一个JSON文件如下:

[
 {
  id: 123,
  textEN: "something",
  textDE: "irgendwas"
 }, 
 ...
]

我的环境中有一个struct:

struct Something: Codable, Identifiable, Hashable {
 var id: Int
 var text: String
}

只解析用户使用的语言的最佳和/或最简单的方法是什么?我可以使用Locale.current.language.languageCode?.identifier检查用户设置的语言。但是我应该如何始终只解析特定的语言?我考虑了以下方法,但偶然发现了一些问题:
1.解析所有不同的语言,并在视图中使用开关情况。可以工作,但在我看来是非常非常糟糕的代码。
1.解析所有不同的语言,并在视图之前实现另一个逻辑层。在视图使用数据之前,管理器或视图模型或其他任何东西将负责删除除用户之外的所有翻译。应该可以工作,但是否有更好的选择?
1.不知何故,只解析和存储当前正在使用的数据中的一种语言。可能是最好的解决方案,但我不能让这个工作。我试图实现一个CodingKey,它是一个连接的字符串(“文本”+语言代码),但它是不可能的,或者至少我不能弄清楚如何使用一个变量作为CodingKey。

xwbd5t1u

xwbd5t1u1#

要只解析用户正在使用的语言,可以使用Decodable结构体的init(from decoder: Decoder)方法来选择性地只解码用户设置的语言。
以下是示例实现:

let json = """
{
  "id": 123,
  "textEN": "something",
  "textDE": "irgendwas"
}
""".data(using: .utf8)!

struct Something: Decodable, Identifiable, Hashable {
    var id: Int
    var text: String
    
    enum CodingKeys: String, CodingKey {
        case id, textEN, textDE
    }

    init(from decoder: Decoder) throws {
        
        // change language value with `Locale.current.languageCode?.identifier`
        let language = "en"
        
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        
        let textKeyForLanguage = language == "en" ? CodingKeys.textEN : CodingKeys.textDE
        self.text = try container.decode(String.self, forKey: textKeyForLanguage)
    }
}

在这个实现中,您使用CodingKeys枚举来定义JSON文件中属性的键。然后,在init(from decoder: Decoder)方法中,您首先获取用户选择的语言,然后使用它来选择性地解码与所选语言对应的属性。
您可以将默认的language值替换为Locale.current.language.languageCode?.identifier,以获取用户选择的语言,但请确保处理未设置或未识别用户语言的情况。
注意,我遵循的是Decodable而不是Codable,所以如果你需要它是Encodable,你也必须实现func encode(to encoder: Encoder) throws
希望这有帮助!

7cwmlq89

7cwmlq892#

您可以尝试这种方法,使用动态键AnyKey(如注解和链接中所述),init(from decoder: Decoder)langKey来只读您想要的语言。
示例代码显示了一种使用class LingoModel: ObservableObject读取json数据的非常简单的方法。根据您自己的目的调整代码。

import Foundation
import SwiftUI

class LingoModel: ObservableObject {
    @Published var langs = [Lingo]()
    
    static var langKey = "textEN"
    
    func fetchData(for lang: String) {
        LingoModel.langKey = lang
        
        let json = """
[
{
"id": 123,
"textEN": "something",
"textDE": "irgendwas"
},
{
"id": 124,
"textEN": "something2",
"textDE": "irgendwas2"
},
{
"id": 125,
"textEN": "something2",
"textDE": "irgendwas2",
"textJA": "日本語"
}
]
"""
        do {
            // simulated data from an API
            let data = Data(json.utf8)
            langs = try JSONDecoder().decode([Lingo].self, from: data)
        } catch {
            print(error)
        }
    }
}

struct ContentView: View {
    @StateObject var model = LingoModel()
    @State var selected: String = "textEN"
    
    var body: some View {
        VStack {
            Picker("", selection: $selected) {
                Text("textEN").tag("textEN")
                Text("textDE").tag("textDE")
                Text("textJA").tag("textJA")
            }.pickerStyle(.segmented).frame(width: 222)
                .onChange(of: selected) { lang in
                    model.fetchData(for: lang)
                }
            List(model.langs) { lingo in
                Text(lingo.text)
            }
        }
        .onAppear {
            model.fetchData(for: selected)
        }
    }
    
}

struct Lingo: Codable, Identifiable, Hashable {
    var id: Int
    var text: String
    
    struct AnyKey: CodingKey {
        var stringValue: String
        var intValue: Int?
        
        init?(stringValue: String) {  self.stringValue = stringValue  }
        init?(intValue: Int) { return nil } // not used
    }
    
    init(from decoder: Decoder) throws {
        let container1 = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container1.decode(Int.self, forKey: .id)
        
        self.text = ""
        let container2 = try decoder.container(keyedBy: AnyKey.self)
        
        if let theKey = container2.allKeys.first(where: {$0.stringValue == LingoModel.langKey}),
           let txt = try? container2.decode(String.self, forKey: theKey) {
            self.text = txt
        }
    }
    
}

相关问题