Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to parse and extract data #3

Closed
FasterThanLlamas opened this issue Jun 4, 2016 · 24 comments
Closed

Unable to parse and extract data #3

FasterThanLlamas opened this issue Jun 4, 2016 · 24 comments

Comments

@FasterThanLlamas
Copy link

Hi Edwin,

Here's a section of the file I'm trying to parse:

<allgames>
<StateProv stateprov_name="Arkansas" stateprov_id="AR">
<game game_id="101" game_name="Powerball" update_time="WED 2016-06-01 23:00:34 EST">
<lastdraw_date>06/01/2016</lastdraw_date>
<lastdraw_numbers>23-30-33-40-69, Powerball: 12, Power Play: 5</lastdraw_numbers>
<nextdraw_date>06/04/2016</nextdraw_date>
<jackpot date="06/04/2016">110000000</jackpot>
</game>
<game game_id="113" game_name="MEGA Millions" update_time="FRI 2016-06-03 23:01:31 EST">
<lastdraw_date>06/03/2016</lastdraw_date>
<lastdraw_numbers>32-54-65-66-71, Mega Ball: 10, Megaplier: 3</lastdraw_numbers>
<nextdraw_date>06/07/2016</nextdraw_date>
<jackpot date="06/03/2016">251000000</jackpot>
</game>
<game game_id="132" game_name="Lucky For Life" update_time="THU 2016-06-02 22:54:02 EST">
<lastdraw_date>06/02/2016</lastdraw_date>
<lastdraw_numbers>11-19-36-37-42, Lucky Ball: 10</lastdraw_numbers>
<nextdraw_date>06/06/2016</nextdraw_date>
</game>
<game game_id="AR1" game_name="Natural State Jackpot" update_time="FRI 2016-06-03 21:19:03 EST">
<lastdraw_date>06/03/2016</lastdraw_date>
<lastdraw_numbers>04-18-19-21-33</lastdraw_numbers>
<nextdraw_date>06/04/2016</nextdraw_date>
<jackpot date="06/04/2016">105000</jackpot>
</game>
<game game_id="ARD" game_name="Cash 4 Midday" update_time="FRI 2016-06-03 14:10:03 EST">
<lastdraw_date>06/03/2016</lastdraw_date>
<lastdraw_numbers>6-0-8-3</lastdraw_numbers>
<nextdraw_date>06/04/2016</nextdraw_date>
</game>
<game game_id="ARB" game_name="Cash 3 Midday" update_time="FRI 2016-06-03 14:09:03 EST">
<lastdraw_date>06/03/2016</lastdraw_date>
<lastdraw_numbers>4-7-4</lastdraw_numbers>
<nextdraw_date>06/04/2016</nextdraw_date>
</game>
<game game_id="ARC" game_name="Cash 4 Evening" update_time="FRI 2016-06-03 20:10:02 EST">
<lastdraw_date>06/03/2016</lastdraw_date>
<lastdraw_numbers>2-6-4-1</lastdraw_numbers>
<nextdraw_date>06/04/2016</nextdraw_date>
</game>
<game game_id="ARA" game_name="Cash 3 Evening" update_time="FRI 2016-06-03 20:12:12 EST">
<lastdraw_date>06/03/2016</lastdraw_date>
<lastdraw_numbers>5-2-0</lastdraw_numbers>
<nextdraw_date>06/04/2016</nextdraw_date>
</game>
</StateProv>

Some sections of my code:

import UIKit

import Alamofire

import AlamofireXmlToObjects

import ObjectMapper

import AlamofireObjectMapper

import EVReflection
class LotteryResponse: EVObject {

        var stateprov_name: String?

        var gameData: [game] = [game]()

    }
 class game: EVObject {

        var game_name: String?

        var lastdraw_date: String?

        var lastdraw_numbers: String?

        var nextdraw_date: String?

        var jackpot: String?

    }
    class AlamofireXmlToObjects {
        func responseObject() {
            let urlString = "http://www.lotterynumbersxml.com/lotterydata/fasterthanllamas@gmail.com-tset/kjw8uwe/lottery_us.xml"

            Alamofire.request(.GET, urlString, parameters: nil)
                .responseObject { (response: Result< LotteryResponse, NSError>) in
                    if let LotteryResponse = response.value {

                    }
            }

        }
    }

@evermeer
Copy link
Owner

evermeer commented Jun 5, 2016

When starting from scratch, then the first step will be defining the root level class without any properties like this:

class AllGames: EVObject {
    var __name: String?
}

Then run a base test where you try to parse the XML to that object model with code like this:

Alamofire.request(.GET, URL).responseObject { (response: Result<AllGames, NSError>) in
The test will succeed. You will only get the warning:

WARNING: The class 'AllGames' is not key value coding-compliant for the key 'StateProv'
but besides that, you would like to see what other properties you should add. You can see the dictionary representation of the XML by adding the line:

Request.outputDictionary = true

If you then run the test again, you can see more fields that you should map. In the output you will now see:

Dictionary from XML = {
    StateProv =     {
        "_stateprov_id" = AR;
        "_stateprov_name" = Arkansas;
        game =         (
                        {
                "_game_id" = 101;
                "_game_name" = Powerball;
                "_update_time" = "WED 2016-06-01 23:00:34 EST";
                jackpot =                 {
                    "__text" = 110000000;
                    "_date" = "06/04/2016";
                };
                "lastdraw_date" = "06/01/2016";
                "lastdraw_numbers" = "23-30-33-40-69, Powerball: 12, Power Play: 5";
                "nextdraw_date" = "06/04/2016";
            },
                        {
                "_game_id" = 113;
                "_game_name" = "MEGA Millions";
                "_update_time" = "FRI 2016-06-03 23:01:31 EST";
                jackpot =                 {
                    "__text" = 251000000;
                    "_date" = "06/03/2016";
                };
                "lastdraw_date" = "06/03/2016";
                "lastdraw_numbers" = "32-54-65-66-71, Mega Ball: 10, Megaplier: 3";
                "nextdraw_date" = "06/07/2016";
            },
        … more game data … 
        );
    };
    "__name" = allgames;
}

So here you can see that you need an extra property for StateProv. In the output you can also see that it has an attribute for _stateprov_id and _stateprov_name and that it has an array of game elements below that. We will add those to the classes we have. And since we see what should be in the game class, we also will make an initial setup for that:

class AllGames: EVObject {
    var __name: String?
    var StateProv: StateProvObject?
}

class StateProvObject: EVObject {
    var __name: String?
    var _stateprov_name: String?
    var _stateprov_id: String?
    var game: [Game] = []
}

class Game: EVObject {
    var __name: String?
    var _game_id: Int = 0
    var _game_name: String?
    var _update_time: String?
    var lastdraw_date: String?
    var lastdraw_numbers: String?
    var nextdraw_date: String?
    var jackpot: Jackpot?
}

class Jackpot: EVObject {
    var __text: String?
    var _date: String?
}

If you now run the test, then you can see that all values are set.

You probably do notice that all dates are put into a string. I did that because the standard date formatter is not right in your case. So you have to find out the correct dateformatter and use that. I tried it with the code below, but that does not work. I have to leave now, so I will let it up to you to fix this :)

        let dateFormatter = NSDateFormatter()
        dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
        dateFormatter.dateFormat = "ddd yyyy'-'MM'-'dd' 'HH':'mm':'ss Z"
        EVReflection.setDateFormatter(dateFormatter)

You can now find this solution as a unit test in the project.

@evermeer
Copy link
Owner

evermeer commented Jun 5, 2016

You can find the test here:

@FasterThanLlamas
Copy link
Author

Thanks so much. This really helps. I'm not able to import XCTest though, so it's throwing a bunch of errors.

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

Could you try updating the pod?
Or download the code from:
https://github.com/evermeer/AlamofireXmlToObjects/archive/master.zip

@FasterThanLlamas
Copy link
Author

Pod updating works fine. XCTest is showing "cannot import underlying module". I did some Googling and it didn't help.

@FasterThanLlamas
Copy link
Author

screen shot 2016-06-06 at 06 11 28

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

Ah, XCTest is part of the Apple unit test framework. I see that you imported the code into your view controller. You could do that, but then you have to remove the import XCTest and change all the XCTAssert* by other checks. And then... Is AlamofireXMLToObjects a dependency for your app (added by cocoapods)? It should automatically import EVReflection, XMLDictionary and Alamofire

@FasterThanLlamas
Copy link
Author

Yes, I have it in my podfile.

use_frameworks!

pod 'AlamofireXmlToObjects'
pod 'ObjectMapper'
pod 'AlamofireObjectMapper'

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

When i search for the error 'unable to read module map' then I find this solution:

Turns out it was an xcode problem. Xcode was using another projects project path, even though this was a new project. Closing Xcode and deleting the xcode preferences from ~/Library/Preferences/ fixed the issue.

I'm leaving this here in case someone on the internet comes across it.

Besides that... Did you open the workspace file instead of the project?

@FasterThanLlamas
Copy link
Author

Tried that and still getting all those errors. It seems like a problem with XCTest. The calls related to that (XCTestCase or XCTAsset) are still showing Unresolved Identifier.

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

I do think this is a generic pod issue with your project. You also have the same issue with Objectmapper. When you look at your project tree. Do you also have a Pods project besides your own project? In the screenshot below you se the Pods project with 3 pods. You should also see a couple of other pods like ObjectMapper and AlamofireXMLToObject

screen shot 2016-06-06 at 8 45 54 pm

@FasterThanLlamas
Copy link
Author

Yep, I think they're all there.
screen shot 2016-06-06 at 11 57 52

@FasterThanLlamas
Copy link
Author

That definitely fixed the module problems, but there still a bug with XCTest.

screen shot 2016-06-06 at 12 32 04

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

Great,
That's because XCTest is from the Apple testing framework. You are running the code from your app. The testing framework is not referenced in your app. You should remove the line 'import XCTest' and then also remove all XCAssert* statements.

@FasterThanLlamas
Copy link
Author

Ok, now I'm really confused. I don't have an 'import XCTest' and all the XCTAssert statements are from the code you sent earlier.

@FasterThanLlamas
Copy link
Author

Do I need the tests in there to get the XML parsing to work correctly?

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

no you don't need it in your actual code, it's just additional code to show in a unit test that the code works. The 'import XCTest' will make the testing framework available. Ale XCAssert statements will do some sort of comparison to validate results. When removing all test related code, you will end up with code like this:

import Alamofire
import XMLDictionary
import EVReflection

class AllGames: EVObject {
    var __name: String?
    var StateProv: StateProvObject?
}

class StateProvObject: EVObject {
    var __name: String?
    var _stateprov_name: String?
    var _stateprov_id: String?
    var game: [Game] = []
}

class Game: EVObject {
    var __name: String?
    var _game_id: Int = 0
    var _game_name: String?
    var _update_time: NSDate?
    var lastdraw_date: String?
    var lastdraw_numbers: String?
    var nextdraw_date: String?
    var jackpot: Jackpot?
}

class Jackpot: EVObject {
    var __text: String?
    var _date: String?
}

class AlamofireXmlToObjects3Tests {

    func testResponseObject() {
        // This is an example of a functional test case.
        let URL: URLStringConvertible = "http://raw.githubusercontent.com/evermeer/AlamofireXmlToObjects/master/AlamofireXmlToObjectsTests/sample3_xml"

        Request.outputDictionary = true

        let dateFormatter = NSDateFormatter()
        dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
        dateFormatter.dateFormat = "ddd yyyy'-'MM'-'dd' 'HH':'mm':'ss Z"
        EVReflection.setDateFormatter(dateFormatter)

        Alamofire.request(.GET, URL)
            .responseObject { (response: Result<AllGames, NSError>) in
                expectation.fulfill()
                if let error = response.error {
                    // Add your code here for handeling the error
                } else {
                    if let result = response.value {
                        print("\(result.description)")
                    } else {
                        // Add your code here for showing that there was no result
                    }
                }
        }
    }

}

@FasterThanLlamas
Copy link
Author

So close!! Only 2 more errors!!

screen shot 2016-06-06 at 13 36 58

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

:-) The unit test was copied from the AlamofireXMLToObjects project. When using the code from an other project that imports AlamofireXMLToObjects as a pod, then you also have to add an import statement for that. So add the following line to your code:

import AlamofireXMLToObjects

@FasterThanLlamas
Copy link
Author

Unfortunately, that didn't clear those errors.

@FasterThanLlamas
Copy link
Author

Sorry, the second error changed.

screen shot 2016-06-06 at 14 19 58

@evermeer
Copy link
Owner

evermeer commented Jun 6, 2016

the expectation.fulfill is a left over from the test.... You can remove that line.

The Request.outputDictionary is added in the latest version of AlamofireXMLToObjects. You should run a 'pod update' . Actually now that the object structure is there you could also remove that line. The only thing that line does is print the dictionary that was created from the XML

@FasterThanLlamas
Copy link
Author

That's it! Thank you so much for the help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants