I have been trying to deserialized a nested XML with SWXMLHash, I was wordering if someone could help me ? I apologize in advance, I'm new to this stuff. Thanks in advance for helping.
here is an extract of the XML :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<kml
xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"
xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>Stations</name>
<Style id="AVAILABLE">
<IconStyle>
<scale>1.0</scale>
<heading>0.0</heading>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/grn-circle.png</href>
<refreshInterval>0.0</refreshInterval>
<viewRefreshTime>0.0</viewRefreshTime>
<viewBoundScale>0.0</viewBoundScale>
</Icon>
</IconStyle>
</Style>
<Style id="UNAVAILABLE">
<IconStyle>
<scale>1.0</scale>
<heading>0.0</heading>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/red-circle.png</href>
<refreshInterval>0.0</refreshInterval>
<viewRefreshTime>0.0</viewRefreshTime>
<viewBoundScale>0.0</viewBoundScale>
</Icon>
</IconStyle>
</Style>
<Placemark>
<name>Esch-sur-Alzette - Parking souterrain Brill - Place de la Résistance</name>
<visibility>1</visibility>
<address>Rue Louis Pasteur, L-4276 Esch-sur-Alzette Luxembourg</address>
<description><span><b>4</b> connectors with 22kW and Type 2 connector<span><br/><span><b>4</b> available connectors<span><br/><span><b>0</b> occupied connectors<span><br/></description>
<styleUrl>#AVAILABLE</styleUrl>
<ExtendedData>
<Data name="CPnum">
<displayName>Number of chargingpoints</displayName>
<value>4</value>
</Data>
<Data name="chargingdevice">
<displayName>Charging device</displayName>
<value>{"id":10644,"name":"CP2500","numberOfConnectors":2,"connectors":[{"id":59985,"name":"CP2500 - 1","maxchspeed":22.08,"connector":1,"description":"AVAILABLE"},{"id":59986,"name":"CP2500 - 2","maxchspeed":22.08,"connector":2,"description":"AVAILABLE"}]}</value>
</Data>
<Data name="chargingdevice">
<displayName>Charging device</displayName>
<value>{"id":10645,"name":"CP2501","numberOfConnectors":2,"connectors":[{"id":59987,"name":"CP2501 - 1","maxchspeed":22.08,"connector":1,"description":"AVAILABLE"},{"id":59988,"name":"CP2501 - 2","maxchspeed":22.08,"connector":2,"description":"AVAILABLE"}]}</value>
</Data>
</ExtendedData>
<Point>
<altitudeMode>clampToGround</altitudeMode>
<coordinates>5.97622,49.492728</coordinates>
</Point>
</Placemark>
here is the struct and the function I'm using :
`
struct XMLPlacemark: XMLObjectDeserialization {
var name: String
var visibility: Int
var address: String
var description: String
var styleUrl: String
var cpnumber : Int
var chargingdevice : [String]
static func deserialize(_ node: XMLIndexer) throws -> XMLPlacemark {
return try XMLPlacemark(
name: node["name"].value(),
visibility: node["visibility"].value(),
address: node["address"].value(),
description: node["description"].value(),
styleUrl: node["styleUrl"].value(),
cpnumber: node["ExtendedData"]["Data"].withAttribute("name", "CPnum")["value"].value(),
chargingdevice: node["ExtendedData"]["Data"].withAttribute("name","chargingdevice")["value"].value()
)
}
}
func loadtest() {
let xml = XMLHash.parse(stationXML)
if let station: [XMLPlacemark] = try? xml["kml"]["Document"]["Placemark"].value() {
station.forEach {station in
print("\(station.chargingdevice)")
}
}
}
Here is the result I get :
["{\"id\":10644,\"name\":\"CP2500\",\"numberOfConnectors\":2,\"connectors\":[{\"id\":59985,\"name\":\"CP2500 - 1\",\"maxchspeed\":22.08,\"connector\":1,\"description\":\"AVAILABLE\"},{\"id\":59986,\"name\":\"CP2500 - 2\",\"maxchspeed\":22.08,\"connector\":2,\"description\":\"AVAILABLE\"}]}"]
I'm missing the other value of the node :
<value>{"id":10645,"name":"CP2501",....
Any idea ?
CodePudding user response:
Okay, so I understand what the issue here. The withAttribute
method only returns a single attribute match, not all matches. That's why it is only returning the first match. You can see the implementation here: https://github.com/drmohundro/SWXMLHash/blob/main/Source/XMLIndexer.swift#L159.
Because you're wanting to filter on these types, you'll need to tweak the implementation slightly. Here is a test I wrote that works the way you're expecting:
struct XMLPlacemark: XMLObjectDeserialization {
var name: String
var visibility: Int
var address: String
var description: String
var styleUrl: String
var cpnumber : Int
var chargingdevice : [String]
static func deserialize(_ node: XMLIndexer) throws -> XMLPlacemark {
var devices: [String] = []
for dataElem in node["ExtendedData"]["Data"].all {
if (dataElem.element?.attribute(by: "name")?.text == "chargingdevice") {
devices.append(try dataElem["value"].value())
}
}
return try XMLPlacemark(
name: node["name"].value(),
visibility: node["visibility"].value(),
address: node["address"].value(),
description: node["description"].value(),
styleUrl: node["styleUrl"].value(),
cpnumber: node["ExtendedData"]["Data"].withAttribute("name", "CPnum")["value"].value(),
chargingdevice: devices
)
}
}
func testLoading() {
let xml = XMLHash.parse(xmlWithComplexType)
do {
let station: [XMLPlacemark] = try xml["kml"]["Document"]["Placemark"].value()
station.forEach {station in
print("hello")
print("\(station.chargingdevice)")
}
} catch {
print("failure? \(error)")
}
}