SWXMLHash parse Data object
According to SWXMLHash
public func parse(_ xml: String) -> XMLIndexer {
guard let data = xml.data(using: options.encoding) else {
return .xmlError(.encoding)
}
return parse(data)
}
public func parse(_ data: Data) -> XMLIndexer {
let parser: SimpleXmlParser = options.shouldProcessLazily
? LazyXMLParser(options)
: FullXMLParser(options)
return parser.parse(data)
}
Whereas you give a String
or a Data
object, internally it will parse a Data
object doing a conversion if needed.
It's a safe way of coding, this way all initialization methods pass through the same one, and fixing it fix all the different initialization.
So it should work.
Your issue is that you file is a string representation of HexData, not a RawHexData file.
The solution is to put in that file the XML String instead.
The Data(contentsOf: fileURL, options: .mappedIfSafe)
will convert that XML String into a Hex representation of it.
And then it will work.
Unable to parse XML from Webservice using SWXMLHash
OK. I have solved the problem. The issue was with the XML file i receive from the server. It looks like it is UTF-8 format, but the document says it is UTF-16. So, I now convert the data object received into a UTF-8 NSString, and then cast that into a String! Please see the code below:
let baseUrl = "http://apps.hha.co.uk/mis/Api/getlivesensors.aspx?key=6fb21537-fb4e-4fe4-b07a-d8d68567c9d1"
var request = URLRequest(url: NSURL(string: baseUrl)! as URL)
let session = URLSession.shared
request.httpMethod = "GET"
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
if data == nil {
print("dataTaskWithRequest error: \(error)")
return
}
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)?.replacingOccurrences(of: "utf-16", with: "utf-8")
let xml = SWXMLHash.parse(dataString! as String)
//let xml = SWXMLHash.parse(dataString!)
if (xml["Sensors"]["Sensor"][0]["Name"].element?.text) != nil
{
self.sensors.add(xml["Sensors"]["Sensor"][0]["Name"].element?.text as Any)
}
DispatchQueue.main.async(execute : {
print(self.sensors)
})
}
task.resume()
// but obviously don't try to use it here here
Deserialisation error with SWXMLHash
You're almost there, just one small mistype... instead of XMLElementDeserializable
, you should use XMLIndexerDeserializable
.
The below works for me in a Playground:
import SWXMLHash
let xmlData = "<Root>" +
"<SomeData>Some value</SomeData>" +
" <ClientInfo>" +
" <Client>MD</Client>" +
" <Name>Massive Dynamic</Name>" +
" </ClientInfo>" +
"</Root>"
let xml = SWXMLHash.parse(xmlData)
struct ClientInfo : XMLIndexerDeserializable {
let Client: String
let Name: String
static func deserialize(_ node: XMLIndexer) throws -> ClientInfo {
return try ClientInfo(
Client: node["Client"].value(),
Name: node["Name"].value()
)
}
}
let info: ClientInfo? = try? xml.children[0]["ClientInfo"].value()
The distinction between those two gets a little confusing and it definitely needs to be improved in the library. The gist is that XMLElementDeserializable
is for standalone types (e.g. strings, ints, dates, etc.) that don't have children nodes.
Can't return value parsing XML using SWXMLHash in Swift
Firt thing: you forgot to include ["product"]
at the beginning of your query.
Then your last subscripting with [type="probability_of_precipitation"]
wasn't using the right syntax.
Here's two different ways of getting your value.
1- By index:
let precipForecast = xml["product"]["forecast"]["area"][2]["forecast-period"][0]["text"][1].element?.text
2- By attributes, with .withAttr
. In your case, for "type":
do {
let precipForecast = try xml["product"]["forecast"]["area"][2]["forecast-period"][0]["text"].withAttr("type", "probability_of_precipitation").element?.text
} catch {
print(error)
}
or if your want an Optional:
let precipForecast = try? xml["product"]["forecast"]["area"][2]["forecast-period"][0]["text"].withAttr("type", "probability_of_precipitation").element?.text
Good parser to parse large XML file and handling the parsed data in Swift
If:
This data does not always need to be updated, only on occasion.
Core data can scale effectively to a very large number of small entries.
Then:
Use whichever XMLParser is fastest with large data sets.
Parse the downloaded data, and store the parsed data into Core Data (possibly create new entries per XML key:value pair).
After the initial download of the XML file, only check the status code on subsequent accesses (304 means that nothing has changed).
When populating the table, you can use Core Data to access the individual properties without loading the whole file into memory.
Any easier way to convert XML to Plist?
You could make your Item
conform to Encodable
struct Item: XMLIndexerDeserializable, Encodable {
let name: String
let enabled: String
let schema_ver: String
let ver: String
let actions: Actions // this property would have to be Encodable as well
}
struct Actions: Encodable {...}
Then you don't have to use a dictionary, pretty much same as you had it.
func convertToPlist(item: Item) {
let encoder = PropertyListEncoder()
encoder.outputFormat = .binary
do {
let data = try encoder.encode(item)
let fileURL = URL(<where you want to save plist>)
try data.write(to: fileURL)
} catch {
// Handle error
print(error)
}
}
Not sure on making the output smaller. PropertyListEncoder
has a few different output options. .binary
might decrease the size a bit.
encoder.outputFormat = .binary
Related Topics
Prevent Retain Cycle in Swift Function Pointers
How to Make a Function Operate on a Sequence of Optional Values
Passing Values Between Viewcontrollers Based on List Selection in Swift
Continuous Rotation of Nsimageview (So It Appears to Be Animated)
How to Add UIpickerview in UIalertcontroller
How to Navigate from Initial UIviewcontroller to UIsplitviewcontroller in Swift
Changing a Label in Prepareforsegue
Open View Controller When Remote Notification Pressed
Swift: Benefits of Curry Function
Swift: Get The Compile Time Name of Variable (Referencing to a Class)
Swift Preserve UIswitch State on UIlongpress
Argument of '#Selector' Does Not Refer to an '@Objc' Method (Swift 3)