Search code examples
iosxmlswiftswxmlhash

Handling missing properties when deserializing xml using SWXMLHash


I've been following the examples at SWXMLHash to deserialize an XML file. It is working quite well, but I am not sure how to handle cases when the XML input is incomplete:

For example, suppose the XML input is this:

<shippingInfo>
            <shippingServiceCost currencyId="USD">0.0</shippingServiceCost>
            <shippingType>Free</shippingType>
            <shipToLocations>US</shipToLocations>
            <expeditedShipping>true</expeditedShipping>
            <oneDayShippingAvailable>false</oneDayShippingAvailable>
            <handlingTime>1</handlingTime>
</shippingInfo>

To deserialize this XML, I create the following struct that is an XMLIndexerDeserializable

import SWXMLHash

struct ShippingInfo: XMLIndexerDeserializable
{
    let currencyId: String
    let shippingServiceCost: Double
    let shippingType: String
    let shipToLocations: String
    let expeditedShipping: Bool
    let oneDayShippingAvailable: Bool
    let handlingTime: Int

    static func deserialize(_ node: XMLIndexer) throws -> ShippingInfo 
    {
        return try ShippingInfo(
            currencyId: node["shippingServiceCost"].value(ofAttribute: "currencyId"),
            shippingServiceCost: node["shippingServiceCost"].value(),
            shippingType: node["shippingType"].value(),
            shipToLocations: node["shipToLocations"].value(),
            expeditedShipping: node["expeditedShipping"].value(),
            oneDayShippingAvailable: node["oneDayShippingAvailable"].value(),
            handlingTime: node["handlingTime"].value()
        )
    }
}

The code above works, until the shippingInfo XML misses an element, like so:

<shippingInfo>
            <shippingServiceCost currencyId="USD">0.0</shippingServiceCost>
            <shippingType>Free</shippingType>
            <shipToLocations>Worldwide</shipToLocations>
            <expeditedShipping>false</expeditedShipping>
            <oneDayShippingAvailable>false</oneDayShippingAvailable>
</shippingInfo>

The 2nd XML above is missing the property "handlingTime". Running the deserialization code above would throw an exception at node["handlingTime"].value()

One approach to fix this problem is to try to catch the exception whenever we access the XMLIndexer's key, and pass in a default value to the property if the exception is thrown which means the key isn't there. I don't think this is best approach though.

What is the best approach to deserialize XML when the XML is missing properties?


Solution

  • Change the declaration of your handlingTime property from Int to Int?, like so:

    let handlingTime: Int?
    

    It needs to be nullable so that the deserialization can support a value that doesn't exist.

    Hope this helps!