Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map to a bool based on the existence of a tag #15

Closed
bhirt opened this issue Jul 28, 2018 · 2 comments
Closed

Map to a bool based on the existence of a tag #15

bhirt opened this issue Jul 28, 2018 · 2 comments

Comments

@bhirt
Copy link

bhirt commented Jul 28, 2018

I have an xml file that has an element that is a flag. For example, <hasThing/>. If that element exists I want to map it to true, otherwise it should be mapped to false. I didn't see anything in the readme. Is there a way to do this?

Thanks.

@gcharita
Copy link
Owner

@bhirt there is a way but is rather difficult.
The library does not support this out of the box.

First of all, there is the stripEmptyNodes flag in the XMLSerialization default ReadingOptions witch I believe I should remove in the next release. So you have to make the deserialization without this flag:

do {
    let readOptions: XMLSerialization.ReadingOptions = [
        .collapseTextNodes,
        .trimWhiteSpace,
        .prefixedAttributes,
        .alwaysNodeName
    ]
    guard let xmlDict = try XMLSerialization.xmlObject(withString: xmlString, options: readOptions) as? [String: Any] else {
        return
    }
} catch {
    print("Deserialization error: \(error.localizedDescription)")
}

after that you can map the generated xmlDict using map(XML:) function of XMLMapper:

// Instead of TestFlag you should use your model class
let testFlag = XMLMapper<TestFlag>().map(XML: xmlDict)

In your model class to deserialize the flag you can check if the tag exists in the XML dictionary of XMLMap property in mapping(map:) function like:

func mapping(map: XMLMap) {
    hasThing = map.XML.keys.contains("hasThing")
}

To serialize the flag, unfortunately, you can't use the Bool type because will mapped like <hasThing>true</hasThing>, neither you can use a String property because the empty string will mapped like <hasThing></hasThing> and the nil value will be ignored completely. So you have to use a helper XMLMappable class like:

private class HasThing: XMLMappable {
    var nodeName: String!
    
    init() { }
    
    required init?(map: XMLMap) { }
    
    func mapping(map: XMLMap) { }
}

private var privateHasThing: HasThing?

var hasThing: Bool? {
    didSet {
        if hasThing == true {
            privateHasThing = HasThing()
        } else {
            privateHasThing = nil
        }
    }
}

Your model class in the end will look something like this:

class TestFlag: XMLMappable {
    
    private class HasThing: XMLMappable {
        var nodeName: String!
        
        init() { }
        
        required init?(map: XMLMap) { }
        
        func mapping(map: XMLMap) { }
    }
    
    var nodeName: String!
    
    private var privateHasThing: HasThing?
    
    var hasThing: Bool? {
        didSet {
            if hasThing == true {
                privateHasThing = HasThing()
            } else {
                privateHasThing = nil
            }
        }
    }
    
    init() { }
    required init?(map: XMLMap) { }
    
    func mapping(map: XMLMap) {
        privateHasThing <- map["hasThing"]
        hasThing = map.XML.keys.contains("hasThing")
    }
    
}

This is not pretty but will work in both serialization and deserialization.

Hope this workaround will help you.

@bhirt
Copy link
Author

bhirt commented Jul 30, 2018

Thank you very much, I appreciate you help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants