3

Using the library from here. Here are the details,

XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <status code="25">Verification required</status>
    <parsed-challenge>
        <action type-id="11">
            <url content-type="application/x-www-form-urlencoded" method="POST" type-id="1">https://example.com</url>
            <hidden-fields>
                <apiRequest>MIAGCSqGSIb3DQEHA6CAMIACAQAxggFAMIIBPAIBAD</apiRequest>
            </hidden-fields>
        </action>
    </parsed-challenge>
    <return-url>https://example.com</return-url>
</root>

Structure:

struct Secure: XMLMappable {

    internal(set) var statusCode: Int?
    internal(set) var status: String?
    internal(set) var actionType: Int?
    internal(set) var url: URLInfo?
    internal(set) var hiddenFields: [String:String]?
    internal(set) var returnURL: Foundation.URL?

    public var nodeName: String!

    public init(map: XMLMap) {
        statusCode = map.value()
        status = map.value()
        actionType = map.value()
        url = map.value()
        hiddenFields = map.value()
        returnURL = map.value()
    }

    public mutating func mapping(map: XMLMap) {
        statusCode      <- map["status"].attributes["code"]
        status          <- map["status"].innerText
        actionType      <- map["parsed-challenge.action"].attributes["type-id"]
        url             <- map["parsed-challenge.action.url"]
        hiddenFields    <- map["parsed-challenge.action.hidden-fields"]
        returnURL       <- (map["return-url"], XMLURLTransform())
    }
}

On decoding,

Secure(statusCode: nil, status: nil, actionType: nil, url: Optional(URLInfo(url: Optional(https://example.com), method: Optional("POST"), contentType: Optional("application/x-www-form-urlencoded"), typeId: Optional(1))), hiddenFields: Optional(["__name": "hidden-fields", "apiRequest": "MIAGCSqGSIb3DQEHA6CAMIACAQAxggFAMIIBPAIBAD"]), returnURL: Optional(https://example.com))

What is wrong with status, statusCode and actionType? Why are they not decoding, is it because deeply nested decoding is not possible?

itsji10dra
  • 4,603
  • 3
  • 39
  • 59

1 Answers1

2

I would love my library to be able to work like that. (the map of nested values are pretty clean that way) But currently it is hard to map nested attributes and innerText of elements that have other elements or attributes inside.

So, the suggested model to map your XML is something like this:

struct Secure: XMLMappable {
    public var nodeName: String!

    internal(set) var status: Status?
    internal(set) var action: Action?
    internal(set) var returnURL: Foundation.URL?

    public init(map: XMLMap) { }

    public mutating func mapping(map: XMLMap) {
        status       <- map["status"]
        action       <- map["parsed-challenge.action"]
        returnURL    <- (map["return-url"], XMLURLTransform())
    }
}

struct Status: XMLMappable {
    public var nodeName: String!

    internal(set) var statusCode: Int?
    internal(set) var message: String?

    public init(map: XMLMap) { }

    public mutating func mapping(map: XMLMap) {
        statusCode    <- map.attributes["code"]
        message       <- map.innerText
    }
}

struct Action: XMLMappable {
    public var nodeName: String!

    internal(set) var actionType: Int?
    internal(set) var url: URLInfo?
    internal(set) var hiddenFields: [String:String]?

    public init(map: XMLMap) { }

    public mutating func mapping(map: XMLMap) {
        actionType      <- map.attributes["type-id"]
        url             <- map["url"]
        hiddenFields    <- map["hidden-fields"]
    }
}

struct URLInfo: XMLMappable {
    public var nodeName: String!

    internal(set) var contentType: String?
    internal(set) var method: String?
    internal(set) var typeID: Int?
    internal(set) var url: Foundation.URL?

    public init(map: XMLMap) { }

    public mutating func mapping(map: XMLMap) {
        contentType    <- map.attributes["content-type"]
        method         <- map.attributes["method"]
        typeID         <- map.attributes["type-id"]
        url            <- (map.innerText, XMLURLTransform())
    }
}

For my personal use though (because I know how to hack my library) I could use something like this:

struct Secure: XMLMappable {
    public var nodeName: String!

    internal(set) var statusCode: Int?
    internal(set) var status: String?
    internal(set) var actionType: Int?
    internal(set) var url: URLInfo?
    internal(set) var hiddenFields: [String:String]?
    internal(set) var returnURL: Foundation.URL?

    public init(map: XMLMap) { }

    public mutating func mapping(map: XMLMap) {
        statusCode      <- map["status._code"]
        status          <- map["status.__text"]
        actionType      <- map["parsed-challenge.action._type-id"]
        url             <- map["parsed-challenge.action.url"]
        hiddenFields    <- map["parsed-challenge.action.hidden-fields"]
        returnURL       <- (map["return-url"], XMLURLTransform())
    }
}

struct URLInfo: XMLMappable {
    public var nodeName: String!

    internal(set) var contentType: String?
    internal(set) var method: String?
    internal(set) var typeID: Int?
    internal(set) var url: Foundation.URL?

    public init(map: XMLMap) { }

    public mutating func mapping(map: XMLMap) {
        contentType    <- map.attributes["content-type"]
        method         <- map.attributes["method"]
        typeID         <- map.attributes["type-id"]
        url            <- (map.innerText, XMLURLTransform())
    }
}

Both models will work ok.

I will definitely update this post, if nested mapping improved in next versions.

gcharita
  • 7,729
  • 3
  • 20
  • 37
  • 1
    I like your suggestion, seems that is only clean way available as of now. Loved you hack as well. Thanks for the help !! If in future your library supports this, kindly update this answer. – itsji10dra Sep 05 '18 at 09:17