Ошибка синтаксического анализа XML в Swift

#ios #xml #swift

#iOS #xml #swift

Вопрос:

Я пытаюсь «модернизировать» одно из наших приложений, которое принимает XML-данные из NOAA. Существующее приложение, использующее этот «простой конвертер XML в NSDictionary» (http://troybrant.net/blog/2010/09/simple-xml-to-nsdictionary-converter /) работает как чемпион. Поскольку это работает и поскольку я стремлюсь к максимально возможному преобразованию перестроенного приложения в Swift, я попытался точно преобразовать этот XML в код NSDictionary. К сожалению, я получаю NSXMLParserErrorLineNumber=1, NSXMLParserErrorColumn= 47, NSXMLParserErrorMessage= Пробел, необходимый после общедоступного идентификатора.

URL-адрес источника XML http://forecast.weather.gov/MapClick.php?FcstType=digitalDWMLamp;lat=40.501368amp;lon=-79.865723 Я бы с удовольствием пропустил опцию XML, чтобы избежать головной боли, но NOAA не согласуется с тем, что он представляет в различных форматах данных (DWML, XML, JSON).

Есть идеи, где я ошибаюсь?

Приведенный ниже код, вызывающий объект:

     var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        println("Task completed")
        if(error) {
            // if there is an error in the request, print it to the console
            self.delegate?.didCompleteWithError(error.localizedDescription)
            //println(error.localizedDescription)
            println("oops!")
        }
        var err: NSError?
        if self.responseFormat == "JSON" {
            // blah blah blah ... the code here does work fine. Snipped for brevity
        } else if self.responseFormat == "XML" {
            // Put XML parsing code here
            var error = NSError()
            var xmlReader = XMLDictionary(error: error)
            var xmlResult = xmlReader.dictionaryForXMLData(data, error: error) as NSDictionary
            self.delegate?.didCompleteWithDictionary(xmlResult)
        }
  

«Конвертер XML в NSDictionary:

 //
//  XMLDictionary.swift
//
//

import Foundation

class XMLDictionary: NSObject, NSXMLParserDelegate {

    var dictionaryStack = NSMutableArray()

var textInProgress = ""
let XMLReaderTextNodeKey = "text"
var errorP = NSError()

func dictionaryForXMLData(data: NSData, error: NSError) -> NSDictionary {
   var reader = XMLDictionary(error: error)
    return reader.objectWithData(data, error: error) as NSDictionary
}

func dictionaryForXMLString(string: String, error: NSError) -> NSDictionary {
    var data : NSData = string.dataUsingEncoding(NSUTF8StringEncoding)
    return dictionaryForXMLData(data, error: error)
}

init(error: NSError) {
    self.errorP = error
}

func objectWithData(data:NSData, error:NSError) -> NSDictionary {
    // clear out any old data
    dictionaryStack.removeAllObjects()
    textInProgress = ""

    // Initialize the stack with a fresh dictionary
    dictionaryStack.addObject(NSMutableDictionary())

    // Parse the XML
    var parser = NSXMLParser(data: data)
    parser.delegate = self

    // some more needed
    var success:Bool = parser.parse()

    if success {
        println("parse success!")
    } else {
        println("parse failure!")
    }

    // Return the stack's root dictionary on success
    var resultDict : NSDictionary! = dictionaryStack[0] as NSDictionary
    NSLog(" in parser: %@", resultDict)
    return resultDict as NSDictionary

}

func parser(parser: NSXMLParser!,didStartElement elementName: String!, namespaceURI: String!, qualifiedName : String!, attributes attributeDict: NSDictionary!) {

    // Get the dictionary for the current level in the stack
    var elementCount = dictionaryStack.count - 1
    if elementCount < 0 {
        elementCount = 0
    }
    NSLog("element: %d", elementCount)
    var parentDict = dictionaryStack[elementCount] as NSMutableDictionary

    // Create the child dictionary for the new element and initialize it with the attributes
    var childDict = NSMutableDictionary()
    childDict.addEntriesFromDictionary(attributeDict)

    // If there's already an item for this key, it means we need to create an array
    var existingValue : AnyObject? = parentDict[elementName]! as AnyObject
    if let eValue : NSMutableArray = existingValue as? NSMutableArray {
        var array = NSMutableArray()
        if eValue.isKindOfClass(NSMutableArray) {
            // The array exists, so use it
            array = eValue
        } else {
            array.addObject(eValue)
            // Replace the child dictionary with an array of children dictionaries
            parentDict.setObject(array, forKey: elementName)
        }

        // Add the new child dictionary to the array
        array.addObject(childDict)
    } else {
        // No existing value, so update the dictionary
        parentDict.setObject(childDict, forKey: elementName)
    }

    // Update the stack
    dictionaryStack.addObject(childDict)

}

func parser(parser: NSXMLParser!, didEndElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!) {

    // Update the parent dict with text info
    var elementCount = dictionaryStack.count - 1
    if elementCount < 0 {
        elementCount = 0
    }
    var dictInProgress = dictionaryStack[elementCount] as NSMutableDictionary

    // Set the text property
    if countElements(textInProgress) > 0 {
        // Get rid of leading   trailing whitespace
        dictInProgress.setObject(textInProgress, forKey: XMLReaderTextNodeKey)

        // Reset the text
        textInProgress = ""

    }

    // Pop the current dict
    dictionaryStack.removeLastObject()

}

func parser(parser: NSXMLParser!, foundCharacters string: String!) {
    // Build the text value
    textInProgress  = string
}

func parser(parser: NSXMLParser!, parseErrorOccurred parseError: NSError!) {
    self.errorP = parseError
    NSLog("failure error: %@", parseError)
    }
}
  

Спасибо

РЕДАКТИРОВАТЬ: добавлен код другого файла в соответствии с запросом.

 //
//  GovDataRequest.swift
//  
//
//  Created by Michael Pulsifer (U.S. Department of Labor) on 6/18/14.
//  License: Public Domain
//

import Foundation

protocol GovDataRequestProtocol {
    func didCompleteWithError(errorMessage: String)
    func didCompleteWithDictionary(results: NSDictionary)
}

class GovDataRequest {

    var delegate: GovDataRequestProtocol? = nil

    var APIKey = ""
    var APIHost = ""
    var APIURL = ""
    var responseFormat = "JSON"
    var timeOut = 60.0

    init(APIKey: String, APIHost: String, APIURL:String) {
        self.APIKey = APIKey
        self.APIHost = APIHost
        self.APIURL = APIURL
    }


    func callAPIMethod (#method: String, arguments: Dictionary<String,String>) {
        // Construct the base url based on the provided information
        var url = APIHost   APIURL   "/"   method
        // Start building the query string
        var queryString = ""

        // Where appropriate, add the key
        switch APIHost {
        case "http://api.dol.gov":
            queryString = "?KEY="   APIKey
        case "http://api.census.gov", "http://pillbox.nlm.nih.gov":
            queryString = "?key="   APIKey
        case "http://api.eia.gov", "http://developer.nrel.gov", "http://api.stlouisfed.org", "http://healthfinder.gov":
            queryString = "?api_key="   APIKey
        case "http://www.ncdc.noaa.gov":
            queryString = "?token="   APIKey
        default:
            // do nothing for now
            println("doing nothing for now")
        }

        //Construct the arguments part of the query string
        for (argKey, argValue) in arguments {
            switch APIHost {
            case "http://api.dol.gov":
                // DOL's V1 API has specific formatting requirements for arguments in the query string
                switch argKey {
                case "top", "skip", "select", "orderby", "filter":
                    queryString  = "amp;$"   argKey   "="   argValue
                case "format", "query", "region", "locality", "skipcount":
                    queryString  = "amp;"   argKey   "="   argValue
                default:
                    println("nothing to see here")
                }
            case "http://go.usa.gov":
                // go.usa.gov requires that the apiKey be the 2nd argument
                if countElements(queryString) == 0 {
                    queryString  = "?"   argKey   "="   argValue   "amp;apiKey="   APIKey
                } else {
                    queryString  = "amp;"   argKey   "="   argValue
                }
            default:
                if countElements(queryString) == 0 {
                    queryString  = "?"   argKey   "="   argValue
                } else {
                    queryString  = "amp;"   argKey   "="   argValue
                }
            }

        }

        //If there are arguments, append them to the url
        if countElements(queryString) > 0 {
            url  = queryString
        }

        //DOT FMCSA requires that the key be placed at the end.
        if APIHost == "https://mobile.fmcsa.dot.gov" {
            if countElements(queryString) > 0 {
                url  = "amp;webKey="   APIKey
            } else {
                url  = "?webKey="   APIKey
            }
        }

        /*
            ASSUMPTION: data retrieved is in JSON format.
            TODO: consider situation when XML is received.
        */


            // Send the request to the API and parse
            var urlToPackage = url.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
            println(urlToPackage)
            var urlToSend: NSURL = NSURL(string: urlToPackage)
            var apiSessionConfiguration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
            apiSessionConfiguration.timeoutIntervalForRequest = timeOut
            var session = NSURLSession(configuration:apiSessionConfiguration)
            var request = NSMutableURLRequest(URL:urlToSend)
            request.addValue("application/json",forHTTPHeaderField:"Accept")
            var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
                println("Task completed")
                if(error) {
                    // if there is an error in the request, print it to the console
                    self.delegate?.didCompleteWithError(error.localizedDescription)
                    //println(error.localizedDescription)
                    println("oops!")
                }
                var err: NSError?
                if self.responseFormat == "JSON" {
                    var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: amp;err) as NSDictionary
                    if(err?) {
                        // If there is an error parson JSON, print it to the console
                        NSLog ("Error parsing the JSON")
                    }
                    self.delegate?.didCompleteWithDictionary(jsonResult)
                } else if self.responseFormat == "XML" {
                    // Put XML parsing code here
                    var error = NSError()
                    var xmlReader = XMLDictionary(error: error)
                    var xmlResult = xmlReader.dictionaryForXMLData(data, error: error) as NSDictionary
                    self.delegate?.didCompleteWithDictionary(xmlResult)
                }
                })
            task.resume()
    }


}
  

Комментарии:

1. Пожалуйста, добавьте также XML. Это должен быть действительный XML.

2. Он довольно большой. Настолько большая, что если бы я включил ее, это было бы довольно неприятно. Вот один пример, который, я знаю, должен работать (он появляется в Chrome): forecast.weather.gov /…

3. Похоже, у него нет общедоступного идентификатора, поэтому там также не может быть пробела: en.wikipedia.org/wiki /…

4. Однако здесь все становится странным. Исходный код Objective-C все еще работает.

5. Я думаю, что это даже исходный код Objective-C, выполняемый в NSXMLParser. Это наводит меня на мысль, что каким-то образом настройки по умолчанию отличаются между настройками, созданными в Swift, и настройками в Objective-C. Интересно, что произойдет, если вы сравните настройки в разделе Управление поведением синтаксического анализатора в отладчике между созданным экземпляром Obj-C и созданным экземпляром Swift developer.apple.com/library/mac/documentation/cocoa/reference /…