JSON解码为变体模型Swift4

JSON解码为变体模型Swift4,json,swift4,decoder,Json,Swift4,Decoder,我有两个JSON请求。根据消息头中的MessageCategory,有一个消息头和一个不同的正文。我如何在一个操场上破译这个 第一个游乐场是: import Cocoa var str = "SimplePaymentJSON playground" struct Request :Decodable { var SaleToPOIRequest : SaleToPOIRequestStr } struct MessageHeaderStr : Decodable {

我有两个JSON请求。根据消息头中的MessageCategory,有一个消息头和一个不同的正文。我如何在一个操场上破译这个

第一个游乐场是:

import Cocoa

var str = "SimplePaymentJSON playground"


struct Request :Decodable {
    var SaleToPOIRequest : SaleToPOIRequestStr
}

struct MessageHeaderStr : Decodable {

    var MessageClass : String
    var MessageCategory : String
    var MessageType : String
    var ServiceID : String
    var SaleID : String
    var POIID : String
}

struct PaymentRequestStr :  Decodable {
    var SaleData : SaleData
    var PaymentTransaction : PaymentTransaction
    var PaymentData : PaymentData
}

struct SaleToPOIRequestStr :  Decodable {
    var MessageHeader : MessageHeaderStr
    var PaymentRequest : PaymentRequestStr

}

struct SaleData : Decodable {
    var SaleTransactionID : SaleTransactionID
}

struct PaymentTransaction : Decodable {

    var AmountsReq : AmountsReq
    var TransactionConditions: TransactionConditions

}

struct SaleTransactionID: Decodable {
    var TransactionID : String
    var TimeStamp : String
}

struct AmountsReq: Decodable {
    var Currency : String
    var RequestedAmount : String
}

struct TransactionConditions: Decodable {
    var LoyaltyHandling : String
}

struct PaymentData: Decodable {
    var PaymentType : String
}


let json = """

{
  "SaleToPOIRequest" : {
    "MessageHeader" : {
      "MessageClass": "Service",
      "MessageCategory": "Payment",
      "MessageType": "Request",
      "ServiceID": "642",
      "SaleID": "SaleTermA",
      "POIID": "POITerm1"
    },
    "PaymentRequest": {
      "SaleData": {
        "SaleTransactionID": {
          "TransactionID": "579",
          "TimeStamp": "2009-03-10T23:08:42.4+01:00"
        }
      },
      "PaymentTransaction": {
        "AmountsReq": {
          "Currency": "EUR",
          "RequestedAmount": "104.11"
        },
        "TransactionConditions": { "LoyaltyHandling": "Forbidden" }
      },
      "PaymentData": { "PaymentType": "Normal" }
    }
  }
}

""".data(using: .utf8)!

// let customer = try! JSONDecoder().decode(Customer.self, from: json)
// print(customer)
do {

    let paymentRequest = try JSONDecoder().decode(Request.self, from: json )

    print(paymentRequest)
    print(paymentRequest.SaleToPOIRequest.MessageHeader.MessageType)
    print(paymentRequest.SaleToPOIRequest.PaymentRequest.PaymentTransaction.AmountsReq.Currency)
    print(paymentRequest.SaleToPOIRequest.PaymentRequest.PaymentTransaction.AmountsReq.RequestedAmount)

}

catch let jsonErr {

    print("Error decoding Json", jsonErr)

}
import Cocoa

var str = "LoginJSON playground"


struct Request : Decodable {
    var SaleToPOIRequest : SaleToPOIRequestStr
}

struct MessageHeaderStr : Decodable {

    var MessageClass : String
    var MessageCategory : String
    var MessageType : String
    var ServiceID : String
    var SaleID : String
    var POIID : String
}

struct LoginRequestStr :  Decodable {
    var OperatorLanguage : String
    var OperatorID : String
    var ShiftNumber : String
    var POISerialNumber : String
    var DateTime : String
    var SaleSoftware : SaleSoftwareStr
    var SaleTerminalData : SaleTerminalDataStr
}

struct SaleToPOIRequestStr :  Decodable {
    var MessageHeader : MessageHeaderStr
    var LoginRequest : LoginRequestStr

}

struct SaleSoftwareStr :  Decodable {
    var ProviderIdentification : String
    var ApplicationName : String
    var SoftwareVersion : String
    var CertificationCode : String
}

struct SaleProfileStr : Decodable {
    var GenericProfile : String
    var ServiceProfiles : String
}

struct SaleTerminalDataStr :  Decodable {

    var TerminalEnvironment : String
    var SaleCapabilities : String
    var SaleProfile : SaleProfileStr
}



let json = """

{
  "SaleToPOIRequest": {
    "MessageHeader": {
      "ProtocolVersion": "3.0",
      "MessageClass": "Service",
      "MessageCategory": "Login",
      "MessageType": "Request",
      "ServiceID": "498",
      "SaleID": "SaleTermA",
      "POIID": "POITerm1"
    },
    "LoginRequest": {
      "OperatorLanguage": "de",
      "OperatorID": "Cashier16",
      "ShiftNumber": "2",
      "POISerialNumber": "78910AA46010005",
      "DateTime": "2015-03-08T09:13:51.0+01:00",
      "SaleSoftware": {
        "ProviderIdentification": "PointOfSaleCo",
        "ApplicationName": "SaleSys",
        "SoftwareVersion": "01.98.01",
        "CertificationCode": "ECTS2PS001"
      },
      "SaleTerminalData": {
        "TerminalEnvironment": "Attended",
        "SaleCapabilities": "PrinterReceipt CashierStatus CashierError CashierDisplay CashierInput",
        "SaleProfile": {
          "GenericProfile": "Extended",
          "ServiceProfiles": "Loyalty PIN CardReader"
        }
      }
    }
  }
}

""".data(using: .utf8)!

// let customer = try! JSONDecoder().decode(Customer.self, from: json)
// print(customer)
do {

    let loginRequest = try JSONDecoder().decode(Request.self, from: json )

    print(loginRequest)
    print(loginRequest.SaleToPOIRequest.MessageHeader.ServiceID)
    print(loginRequest.SaleToPOIRequest.LoginRequest.DateTime)
    print(loginRequest.SaleToPOIRequest.LoginRequest.SaleSoftware.CertificationCode)
    print(loginRequest.SaleToPOIRequest.LoginRequest.SaleTerminalData.SaleProfile.GenericProfile)

}

catch let jsonErr {

    print("Error decoding Json", jsonErr)

}
另一个游乐场是:

import Cocoa

var str = "SimplePaymentJSON playground"


struct Request :Decodable {
    var SaleToPOIRequest : SaleToPOIRequestStr
}

struct MessageHeaderStr : Decodable {

    var MessageClass : String
    var MessageCategory : String
    var MessageType : String
    var ServiceID : String
    var SaleID : String
    var POIID : String
}

struct PaymentRequestStr :  Decodable {
    var SaleData : SaleData
    var PaymentTransaction : PaymentTransaction
    var PaymentData : PaymentData
}

struct SaleToPOIRequestStr :  Decodable {
    var MessageHeader : MessageHeaderStr
    var PaymentRequest : PaymentRequestStr

}

struct SaleData : Decodable {
    var SaleTransactionID : SaleTransactionID
}

struct PaymentTransaction : Decodable {

    var AmountsReq : AmountsReq
    var TransactionConditions: TransactionConditions

}

struct SaleTransactionID: Decodable {
    var TransactionID : String
    var TimeStamp : String
}

struct AmountsReq: Decodable {
    var Currency : String
    var RequestedAmount : String
}

struct TransactionConditions: Decodable {
    var LoyaltyHandling : String
}

struct PaymentData: Decodable {
    var PaymentType : String
}


let json = """

{
  "SaleToPOIRequest" : {
    "MessageHeader" : {
      "MessageClass": "Service",
      "MessageCategory": "Payment",
      "MessageType": "Request",
      "ServiceID": "642",
      "SaleID": "SaleTermA",
      "POIID": "POITerm1"
    },
    "PaymentRequest": {
      "SaleData": {
        "SaleTransactionID": {
          "TransactionID": "579",
          "TimeStamp": "2009-03-10T23:08:42.4+01:00"
        }
      },
      "PaymentTransaction": {
        "AmountsReq": {
          "Currency": "EUR",
          "RequestedAmount": "104.11"
        },
        "TransactionConditions": { "LoyaltyHandling": "Forbidden" }
      },
      "PaymentData": { "PaymentType": "Normal" }
    }
  }
}

""".data(using: .utf8)!

// let customer = try! JSONDecoder().decode(Customer.self, from: json)
// print(customer)
do {

    let paymentRequest = try JSONDecoder().decode(Request.self, from: json )

    print(paymentRequest)
    print(paymentRequest.SaleToPOIRequest.MessageHeader.MessageType)
    print(paymentRequest.SaleToPOIRequest.PaymentRequest.PaymentTransaction.AmountsReq.Currency)
    print(paymentRequest.SaleToPOIRequest.PaymentRequest.PaymentTransaction.AmountsReq.RequestedAmount)

}

catch let jsonErr {

    print("Error decoding Json", jsonErr)

}
import Cocoa

var str = "LoginJSON playground"


struct Request : Decodable {
    var SaleToPOIRequest : SaleToPOIRequestStr
}

struct MessageHeaderStr : Decodable {

    var MessageClass : String
    var MessageCategory : String
    var MessageType : String
    var ServiceID : String
    var SaleID : String
    var POIID : String
}

struct LoginRequestStr :  Decodable {
    var OperatorLanguage : String
    var OperatorID : String
    var ShiftNumber : String
    var POISerialNumber : String
    var DateTime : String
    var SaleSoftware : SaleSoftwareStr
    var SaleTerminalData : SaleTerminalDataStr
}

struct SaleToPOIRequestStr :  Decodable {
    var MessageHeader : MessageHeaderStr
    var LoginRequest : LoginRequestStr

}

struct SaleSoftwareStr :  Decodable {
    var ProviderIdentification : String
    var ApplicationName : String
    var SoftwareVersion : String
    var CertificationCode : String
}

struct SaleProfileStr : Decodable {
    var GenericProfile : String
    var ServiceProfiles : String
}

struct SaleTerminalDataStr :  Decodable {

    var TerminalEnvironment : String
    var SaleCapabilities : String
    var SaleProfile : SaleProfileStr
}



let json = """

{
  "SaleToPOIRequest": {
    "MessageHeader": {
      "ProtocolVersion": "3.0",
      "MessageClass": "Service",
      "MessageCategory": "Login",
      "MessageType": "Request",
      "ServiceID": "498",
      "SaleID": "SaleTermA",
      "POIID": "POITerm1"
    },
    "LoginRequest": {
      "OperatorLanguage": "de",
      "OperatorID": "Cashier16",
      "ShiftNumber": "2",
      "POISerialNumber": "78910AA46010005",
      "DateTime": "2015-03-08T09:13:51.0+01:00",
      "SaleSoftware": {
        "ProviderIdentification": "PointOfSaleCo",
        "ApplicationName": "SaleSys",
        "SoftwareVersion": "01.98.01",
        "CertificationCode": "ECTS2PS001"
      },
      "SaleTerminalData": {
        "TerminalEnvironment": "Attended",
        "SaleCapabilities": "PrinterReceipt CashierStatus CashierError CashierDisplay CashierInput",
        "SaleProfile": {
          "GenericProfile": "Extended",
          "ServiceProfiles": "Loyalty PIN CardReader"
        }
      }
    }
  }
}

""".data(using: .utf8)!

// let customer = try! JSONDecoder().decode(Customer.self, from: json)
// print(customer)
do {

    let loginRequest = try JSONDecoder().decode(Request.self, from: json )

    print(loginRequest)
    print(loginRequest.SaleToPOIRequest.MessageHeader.ServiceID)
    print(loginRequest.SaleToPOIRequest.LoginRequest.DateTime)
    print(loginRequest.SaleToPOIRequest.LoginRequest.SaleSoftware.CertificationCode)
    print(loginRequest.SaleToPOIRequest.LoginRequest.SaleTerminalData.SaleProfile.GenericProfile)

}

catch let jsonErr {

    print("Error decoding Json", jsonErr)

}
如何根据MessageHeader中的MessageCategory进行解码

有什么东西和工会有关吗


就我在您的案例中所见,
saletoirequest
包含类型为
PaymentRequestStr
LoginRequestStr
的属性。我建议您使用关联值声明
enum
,以存储
PaymentRequestStr
LoginRequestStr
的实例。检查此代码段:

enum LoginOrPaymentRequest: Decodable {

    case login(LoginRequestStr)
    case payment(PaymentRequestStr)
    case unknown

    private enum CodingKeys: String, CodingKey {

        case LoginRequest
        case PaymentRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let loginRequest = try container.decodeIfPresent(LoginRequestStr.self, forKey: .LoginRequest) {
            self = .login(loginRequest)
        } else if let paymentRequest = try container.decodeIfPresent(PaymentRequestStr.self, forKey: .PaymentRequest) {
            self = .payment(paymentRequest)
        } else {
            self = .unknown
        }
    }
}
同时更改
SaleToPaymentRequest
的实现,添加类型为
LoginPaymentRequest
的属性:

struct SaleToPOIRequest: Decodable {

    let messageHeader: MessageHeaderStr?
    let loginOrPaymentRequest: LoginOrPaymentRequest

    private enum CodingKeys: String, CodingKey {

        case MessageHeader
    }

    private init(messageHeader: MessageHeaderStr?,
                 loginOrPaymentRequest: LoginOrPaymentRequest) {
        self.messageHeader = messageHeader
        self.loginOrPaymentRequest = loginOrPaymentRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(messageHeader: try container.decode(MessageHeaderStr.self, forKey: .MessageHeader),
                  loginOrPaymentRequest: try LoginOrPaymentRequest(from: decoder))
    }
}
struct DataResponse: Decodable {

    let saleToPOIRequest: SaleToPOIRequest?

    private enum CodingKeys: String, CodingKey {
        case SaleToPOIRequest
    }

    private init(saleToPOIRequest: SaleToPOIRequest?) {
        self.saleToPOIRequest = saleToPOIRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(saleToPOIRequest: try container.decode(SaleToPOIRequest.self, forKey: .SaleToPOIRequest))
    }
}
init(from decoder: Decoder, messageCategory: String) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let loginRequest = try container.decodeIfPresent(LoginRequestStr.self, forKey: .LoginRequest), messageCategory == "Login" {
        self = .login(loginRequest)
    } else if let paymentRequest = try container.decodeIfPresent(PaymentRequestStr.self, forKey: .PaymentRequest), messageCategory == "Payment" {
        self = .payment(paymentRequest)
    } else {
        self = .unknown
    }
}
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let messageHeader = try container.decode(MessageHeaderStr.self, forKey: .MessageHeader)
    let loginOrPaymentRequest = try LoginOrPaymentRequest(from: decoder,
                                                          messageCategory: messageHeader.MessageCategory)
    self.init(messageHeader: messageHeader,
              loginOrPaymentRequest: loginOrPaymentRequest)
}
此结构包含有关
SaleToPaireQuest
实例的信息:

struct SaleToPOIRequest: Decodable {

    let messageHeader: MessageHeaderStr?
    let loginOrPaymentRequest: LoginOrPaymentRequest

    private enum CodingKeys: String, CodingKey {

        case MessageHeader
    }

    private init(messageHeader: MessageHeaderStr?,
                 loginOrPaymentRequest: LoginOrPaymentRequest) {
        self.messageHeader = messageHeader
        self.loginOrPaymentRequest = loginOrPaymentRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(messageHeader: try container.decode(MessageHeaderStr.self, forKey: .MessageHeader),
                  loginOrPaymentRequest: try LoginOrPaymentRequest(from: decoder))
    }
}
struct DataResponse: Decodable {

    let saleToPOIRequest: SaleToPOIRequest?

    private enum CodingKeys: String, CodingKey {
        case SaleToPOIRequest
    }

    private init(saleToPOIRequest: SaleToPOIRequest?) {
        self.saleToPOIRequest = saleToPOIRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(saleToPOIRequest: try container.decode(SaleToPOIRequest.self, forKey: .SaleToPOIRequest))
    }
}
init(from decoder: Decoder, messageCategory: String) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let loginRequest = try container.decodeIfPresent(LoginRequestStr.self, forKey: .LoginRequest), messageCategory == "Login" {
        self = .login(loginRequest)
    } else if let paymentRequest = try container.decodeIfPresent(PaymentRequestStr.self, forKey: .PaymentRequest), messageCategory == "Payment" {
        self = .payment(paymentRequest)
    } else {
        self = .unknown
    }
}
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let messageHeader = try container.decode(MessageHeaderStr.self, forKey: .MessageHeader)
    let loginOrPaymentRequest = try LoginOrPaymentRequest(from: decoder,
                                                          messageCategory: messageHeader.MessageCategory)
    self.init(messageHeader: messageHeader,
              loginOrPaymentRequest: loginOrPaymentRequest)
}
如何测试它:

let dataResponse = try JSONDecoder().decode(DataResponse.self, from: json)
print(dataResponse.saleToPOIRequest?.messageHeader?.POIID)
if let loginOrPaymentRequest = dataResponse.saleToPOIRequest?.loginOrPaymentRequest {
    if case .login(let loginRequest) = loginOrPaymentRequest {
        print(loginRequest.OperatorLanguage)
    } else if case .payment(let paymentRequest) = loginOrPaymentRequest {
        print(paymentRequest.PaymentData.PaymentType)
    }
}
我记得您提到需要检查
MessageCategory
的值。在这种情况下,将此功能添加到
LoginOrPaymentRequest

struct SaleToPOIRequest: Decodable {

    let messageHeader: MessageHeaderStr?
    let loginOrPaymentRequest: LoginOrPaymentRequest

    private enum CodingKeys: String, CodingKey {

        case MessageHeader
    }

    private init(messageHeader: MessageHeaderStr?,
                 loginOrPaymentRequest: LoginOrPaymentRequest) {
        self.messageHeader = messageHeader
        self.loginOrPaymentRequest = loginOrPaymentRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(messageHeader: try container.decode(MessageHeaderStr.self, forKey: .MessageHeader),
                  loginOrPaymentRequest: try LoginOrPaymentRequest(from: decoder))
    }
}
struct DataResponse: Decodable {

    let saleToPOIRequest: SaleToPOIRequest?

    private enum CodingKeys: String, CodingKey {
        case SaleToPOIRequest
    }

    private init(saleToPOIRequest: SaleToPOIRequest?) {
        self.saleToPOIRequest = saleToPOIRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(saleToPOIRequest: try container.decode(SaleToPOIRequest.self, forKey: .SaleToPOIRequest))
    }
}
init(from decoder: Decoder, messageCategory: String) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let loginRequest = try container.decodeIfPresent(LoginRequestStr.self, forKey: .LoginRequest), messageCategory == "Login" {
        self = .login(loginRequest)
    } else if let paymentRequest = try container.decodeIfPresent(PaymentRequestStr.self, forKey: .PaymentRequest), messageCategory == "Payment" {
        self = .payment(paymentRequest)
    } else {
        self = .unknown
    }
}
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let messageHeader = try container.decode(MessageHeaderStr.self, forKey: .MessageHeader)
    let loginOrPaymentRequest = try LoginOrPaymentRequest(from: decoder,
                                                          messageCategory: messageHeader.MessageCategory)
    self.init(messageHeader: messageHeader,
              loginOrPaymentRequest: loginOrPaymentRequest)
}
并修改
saletoirequest
init

struct SaleToPOIRequest: Decodable {

    let messageHeader: MessageHeaderStr?
    let loginOrPaymentRequest: LoginOrPaymentRequest

    private enum CodingKeys: String, CodingKey {

        case MessageHeader
    }

    private init(messageHeader: MessageHeaderStr?,
                 loginOrPaymentRequest: LoginOrPaymentRequest) {
        self.messageHeader = messageHeader
        self.loginOrPaymentRequest = loginOrPaymentRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(messageHeader: try container.decode(MessageHeaderStr.self, forKey: .MessageHeader),
                  loginOrPaymentRequest: try LoginOrPaymentRequest(from: decoder))
    }
}
struct DataResponse: Decodable {

    let saleToPOIRequest: SaleToPOIRequest?

    private enum CodingKeys: String, CodingKey {
        case SaleToPOIRequest
    }

    private init(saleToPOIRequest: SaleToPOIRequest?) {
        self.saleToPOIRequest = saleToPOIRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(saleToPOIRequest: try container.decode(SaleToPOIRequest.self, forKey: .SaleToPOIRequest))
    }
}
init(from decoder: Decoder, messageCategory: String) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let loginRequest = try container.decodeIfPresent(LoginRequestStr.self, forKey: .LoginRequest), messageCategory == "Login" {
        self = .login(loginRequest)
    } else if let paymentRequest = try container.decodeIfPresent(PaymentRequestStr.self, forKey: .PaymentRequest), messageCategory == "Payment" {
        self = .payment(paymentRequest)
    } else {
        self = .unknown
    }
}
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let messageHeader = try container.decode(MessageHeaderStr.self, forKey: .MessageHeader)
    let loginOrPaymentRequest = try LoginOrPaymentRequest(from: decoder,
                                                          messageCategory: messageHeader.MessageCategory)
    self.init(messageHeader: messageHeader,
              loginOrPaymentRequest: loginOrPaymentRequest)
}

最后看起来是这样的:

import Cocoa
//
// https://stackoverflow.com/questions/53592884/json-decoding-into-variant-model-swift4/53617310#53617310
//

var str = "LoginJSON or PaymentJSON playground"


let json1 = """

{
  "SaleToPOIRequest": {
    "MessageHeader": {
      "ProtocolVersion": "3.0",
      "MessageClass": "Service",
      "MessageCategory": "Login",
      "MessageType": "Request",
      "ServiceID": "498",
      "SaleID": "SaleTermA",
      "POIID": "POITerm1"
    },
    "LoginRequest": {
      "OperatorLanguage": "de",
      "OperatorID": "Cashier16",
      "ShiftNumber": "2",
      "POISerialNumber": "78910AA46010005",
      "DateTime": "2015-03-08T09:13:51.0+01:00",
      "SaleSoftware": {
        "ProviderIdentification": "PointOfSaleCo",
        "ApplicationName": "SaleSys",
        "SoftwareVersion": "01.98.01",
        "CertificationCode": "ECTS2PS001"
      },
      "SaleTerminalData": {
        "TerminalEnvironment": "Attended",
        "SaleCapabilities": "PrinterReceipt CashierStatus CashierError CashierDisplay CashierInput",
        "SaleProfile": {
          "GenericProfile": "Extended",
          "ServiceProfiles": "Loyalty PIN CardReader"
        }
      }
    }
  }
}

""".data(using: .utf8)!


let json2 = """

{
  "SaleToPOIRequest" : {
    "MessageHeader" : {
      "MessageClass": "Service",
      "MessageCategory": "Payment",
      "MessageType": "Request",
      "ServiceID": "642",
      "SaleID": "SaleTermA",
      "POIID": "POITerm1"
    },
    "PaymentRequest": {
      "SaleData": {
        "SaleTransactionID": {
          "TransactionID": "579",
          "TimeStamp": "2009-03-10T23:08:42.4+01:00"
        }
      },
      "PaymentTransaction": {
        "AmountsReq": {
          "Currency": "EUR",
          "RequestedAmount": "104.11"
        },
        "TransactionConditions": { "LoyaltyHandling": "Forbidden" }
      },
      "PaymentData": { "PaymentType": "Normal" }
    }
  }
}

""".data(using: .utf8)!


// ------------------- LoginRequest Body -------------------


struct LoginRequest :  Decodable {
    var OperatorLanguage : String
    var OperatorID : String
    var ShiftNumber : String
    var POISerialNumber : String
    var DateTime : String
    var SaleSoftware : SaleSoftwareStr
    var SaleTerminalData : SaleTerminalDataStr
}

struct SaleSoftwareStr :  Decodable {
    var ProviderIdentification : String
    var ApplicationName : String
    var SoftwareVersion : String
    var CertificationCode : String
}

struct SaleProfileStr : Decodable {
    var GenericProfile : String
    var ServiceProfiles : String
}

struct SaleTerminalDataStr :  Decodable {

    var TerminalEnvironment : String
    var SaleCapabilities : String
    var SaleProfile : SaleProfileStr
}

// ------------------- PaymentRequest Body -------------------
struct PaymentRequest :  Decodable {
    var SaleData : SaleData
    var PaymentTransaction : PaymentTransaction
    var PaymentData : PaymentData
}

struct SaleData : Decodable {
    var SaleTransactionID : SaleTransactionID
}

struct PaymentTransaction : Decodable {

    var AmountsReq : AmountsReq
    var TransactionConditions: TransactionConditions

}

struct SaleTransactionID: Decodable {
    var TransactionID : String
    var TimeStamp : String
}

struct AmountsReq: Decodable {
    var Currency : String
    var RequestedAmount : String
}

struct TransactionConditions: Decodable {
    var LoyaltyHandling : String
}

struct PaymentData: Decodable {
    var PaymentType : String
}

// ------------------- MessageHeader -------------------

struct MessageHeader: Decodable {

    var MessageClass : String
    var MessageCategory : String
    var MessageType : String
    var ServiceID : String
    var SaleID : String
    var POIID : String
}

// ------------------- LoginOrPaymentRequest -------------------

enum LoginOrPaymentRequest: Decodable {


    case login(LoginRequest)
    case payment(PaymentRequest)
    case unknown

    private enum CodingKeys: String, CodingKey {

        case LoginRequest
        case PaymentRequest
    }
    // basic
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let loginRequest = try container.decodeIfPresent(LoginRequest.self, forKey: .LoginRequest) {
            self = .login(loginRequest)
        } else if let paymentRequest = try container.decodeIfPresent(PaymentRequest.self, forKey: .PaymentRequest) {
            self = .payment(paymentRequest)
        } else {
            self = .unknown
        }
    }
    // with messageCategory
    init(from decoder: Decoder, messageCategory: String) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let loginRequest = try container.decodeIfPresent(LoginRequest.self, forKey: .LoginRequest), messageCategory == "Login" {
            self = .login(loginRequest)
        } else if let paymentRequest = try container.decodeIfPresent(PaymentRequest.self, forKey: .PaymentRequest), messageCategory == "Payment" {
            self = .payment(paymentRequest)
        } else {
            self = .unknown
        }
    }
}

struct SaleToPOIRequest: Decodable {

    let messageHeader: MessageHeader?
    let loginOrPaymentRequest: LoginOrPaymentRequest

    private enum CodingKeys: String, CodingKey {

        case MessageHeader
    }

    private init(messageHeader: MessageHeader?,
                 loginOrPaymentRequest: LoginOrPaymentRequest) {
        self.messageHeader = messageHeader
        self.loginOrPaymentRequest = loginOrPaymentRequest
    }
    /* basic:
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(messageHeader: try container.decode(MessageHeader.self, forKey: .MessageHeader),
                  loginOrPaymentRequest: try LoginOrPaymentRequest(from: decoder))
    }
    */
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let messageHeader = try container.decode(MessageHeader.self, forKey: .MessageHeader)
        let loginOrPaymentRequest = try LoginOrPaymentRequest(from: decoder,
                                                              messageCategory: messageHeader.MessageCategory)
        self.init(messageHeader: messageHeader,
                  loginOrPaymentRequest: loginOrPaymentRequest)
    }
}

struct DataResponse: Decodable {

    let saleToPOIRequest: SaleToPOIRequest?

    private enum CodingKeys: String, CodingKey {
        case SaleToPOIRequest
    }

    private init(saleToPOIRequest: SaleToPOIRequest?) {
        self.saleToPOIRequest = saleToPOIRequest
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(saleToPOIRequest: try container.decode(SaleToPOIRequest.self, forKey: .SaleToPOIRequest))
    }
}


// Test it:
var dataResponse = try JSONDecoder().decode(DataResponse.self, from: json1)
print(dataResponse.saleToPOIRequest?.messageHeader?.MessageCategory as Any)
if let loginOrPaymentRequest = dataResponse.saleToPOIRequest?.loginOrPaymentRequest {
    if case .login(let loginRequest) = loginOrPaymentRequest {
        print("loginRequest.OperatorLanguage = \(loginRequest.OperatorLanguage)")
    } else if case .payment(let paymentRequest) = loginOrPaymentRequest {
        print("paymentRequest.PaymentData.PaymentType = \(paymentRequest.PaymentData.PaymentType)")
    }
}

dataResponse = try JSONDecoder().decode(DataResponse.self, from: json2)
print(dataResponse.saleToPOIRequest?.messageHeader?.MessageCategory as Any)
if let loginOrPaymentRequest = dataResponse.saleToPOIRequest?.loginOrPaymentRequest {
    if case .login(let loginRequest) = loginOrPaymentRequest {
        print("loginRequest.OperatorLanguage = \(loginRequest.OperatorLanguage)")
    } else if case .payment(let paymentRequest) = loginOrPaymentRequest {
        print("paymentRequest.PaymentData.PaymentType = \(paymentRequest.PaymentData.PaymentType)")
    }
}

请遵守变量以小写字母开头的命名约定。这避免了诸如
var PaymentData:PaymentData
之类的歧义/混乱,非常感谢您的帮助。