1

Im trying to parse JSON data from an RestAPI that gives me energy data for Norway (https://driftsdata.statnett.no/restapi/ProductionConsumption/GetLatestDetailedOverview)

<ProductionConsumptionOverviewViewModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Statnett.Driftsdata.RestApi.Models">
<ConsumptionData>
...
</ConsumptionData>
<Headers>
...
</Headers>
<HydroData>
<ProdConsOverViewModelItem>
<style>hydro</style>
<textTranslationId>General.Hydro</textTranslationId>
<titleTranslationId i:nil="true"/>
<value/>
</ProdConsOverViewModelItem>
<ProdConsOverViewModelItem>
<style i:nil="true"/>
<textTranslationId i:nil="true"/>
<titleTranslationId>ProductionConsumption.HydroSEDesc</titleTranslationId>
<value>4 840</value>

I know it reads like XML but the documentation said JSON so here goes. Im having the same issues reading it as XML still so.

I manage to read the JSON response fine, but Im having trouble "digging down" to the correct spot since the identifiers are the same for the different regions. Let say I wanted the data for the Hydro production below (see screenshot). How would i get that? Ive tries setting the [titleTranslationId] == "ProductionConsumption.HydroSEDesc" but that didnt work.

It looks like XML but the documentation said JSON ? which is why im trying to treat it as JSON.

<ProductionConsumptionOverviewViewModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Statnett.Driftsdata.RestApi.Models">
<ConsumptionData>
...
</ConsumptionData>
<Headers>
...
</Headers>
<HydroData>
<ProdConsOverViewModelItem>
<style>hydro</style>
<textTranslationId>General.Hydro</textTranslationId>
<titleTranslationId i:nil="true"/>
<value/>
</ProdConsOverViewModelItem>
<ProdConsOverViewModelItem>
<style i:nil="true"/>
<textTranslationId i:nil="true"/>
<titleTranslationId>ProductionConsumption.HydroSEDesc</titleTranslationId>
<value>4 840</value>

API response Screenshot

Im using SwiftyJSON to handle the response.

I found some other treads but couldnt get it to work for me. Anyone able to help? Heres my code:

func getEnergyData(url: String){

AF.request(url, method: .get).responseJSON{ response in
    switch response.result {
    case .success(let json):
    
        print("json success")
        //print(json)
        let energyJSON : JSON = JSON(response.result)
        self.updateEnergyData(json: energyJSON)
   
    
    case .failure(let error):
        print(error)

    }
    
}


    
}

trying to parse it:

func updateEnergyData(json : JSON){
        
if let results = json["ProductionConsumptionOverviewViewModel"]["HydroData"]["ProdConsOverViewModelItem"]["value"].double{
    print(results)

}
else{
    print("parse fail")
} 
}       
}
Nisse82
  • 31
  • 7
  • That screenshot is xml not json are you aware of that? You should really move to using the built in json functionality by using `Codable`, it is more modern and it will be easier to get help here at stackoverflow – Joakim Danielson Jul 19 '20 at 13:10
  • yes, ive tried xml parsing several times, and couldnt get it to work at all. the documentation said "data as json" so i figured it was :P https://driftsdata.statnett.no/restapi/ – Nisse82 Jul 19 '20 at 13:13
  • Yes but why are you then posting xml as part of your question? And post sample data as text, not as images – Joakim Danielson Jul 19 '20 at 13:15
  • the api respons is super long and i figured this would make it easier to see. i posted the link to the api in the text above. – Nisse82 Jul 19 '20 at 13:17
  • im sorry if i messed up terribly, im new to this forum. sorry – Nisse82 Jul 19 '20 at 13:17

1 Answers1

0

When accessing this endpoint via a browser, the API returns XML, but otherwise it returns JSON.

No need for SwiftyJSON or other libraries, instead use Swift's Codable protocol.

For example, make a struct representing the object to decode:

struct HydroData: Decodable {
    let value: String
    let textTranslationId: String?
    let titleTranslationId: String?
    let style: String?
}

And another one to get these objects from their array:

struct HydroResult: Decodable {
    let HydroData: [HydroData]
}

Then once you have downloaded the data from the endpoint, decode it, and use filter to find, in the array, the object you need:

let url = URL(string: "https://driftsdata.statnett.no/restapi/ProductionConsumption/GetLatestDetailedOverview")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    guard let data = data else {
        print("No data")
        return
    }
    do {
        let result = try JSONDecoder().decode(HydroResult.self, from: data)
        if let seDesc = result.HydroData.filter({ $0.titleTranslationId == "ProductionConsumption.HydroSEDesc" }).first {
            print(seDesc.value)
        } else {
            print("Error: no value")
        }
    } catch {
        print(error.localizedDescription)
    }
}
task.resume()

Note that the return type for value is a String, and in it the numbers are already formatted, and because of that you cannot directly convert them to Int or Double, you would have to use a custom formatter, but this is an entirely different topic.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • thank you eric, however it keeps complaining that it "Cannot convert value of type 'Data.Type' to expected argument type 'Data'" on JSONDecoder().decode(HydroResult.self, from: data) how do i remedy this? – Nisse82 Jul 19 '20 at 16:10
  • @Nisse82 This is because your `data` is not the right type. I have edited my answer with a more complete example so that you can see how it works. No need for AlamoFire or SwiftyJSON or anything else. – Eric Aya Jul 19 '20 at 16:17
  • thnak you for all the help, however im not getting the "print(seDesc.value)" to execute. i tried adding a "print("values are: \(seDesc.value)")" to see if the code ran but no. im thinking the JSON decoder doesnt work properly? – Nisse82 Jul 19 '20 at 16:30
  • @Nisse82 Just try the example code in a new project, so that you can see it working. Then, adapt to your own codebase. See a screenshot of my example: https://www.dropbox.com/s/1mq11n50bwln9a9/ViewController.swift%202020-07-19%2018-32-12.png?dl=0 this gives me a value of "6 052". – Eric Aya Jul 19 '20 at 16:34
  • haha, arg, damn it your correct. that means i messed up with something else :P. thank you for helping out, now its time to go error hunting :) – Nisse82 Jul 19 '20 at 16:38
  • do you have any resources for how to create a custom formatter to get teh values as an integer? – Nisse82 Jul 19 '20 at 19:04
  • @Nisse82 If the only formatting is the space (like in "6 052") then just add the last extension from https://stackoverflow.com/a/34294660/2227743 to your project then safely unwrap `Int(seDesc.value.digits)`. – Eric Aya Jul 19 '20 at 19:56
  • @Nisse82 But please note that this will only work for removing spaces in the numbers. Anything else (commas, minus signs for negative numbers, other punctuation, etc) will fail. In that case I would suggest you ask a new Question, referencing this page and the one I linked, explaining your issue and what you already tried to fix it. – Eric Aya Jul 19 '20 at 20:06