2

是否可以在 NSXMLparser 中给出路径表达式?我有一个 XML 文件,它有几个相同的名称标签,但它们在不同的元素中。有什么方法可以区分它们。这是 XML:

<Schools>
        <School>
            <ID>335823</ID> 
            <Name>Fairfax High School</Name> 
            <Student>
                <ID>4195653</ID>
                <Name>Will Turner</Name>
            </Student>
            <Student>
                <ID>4195654</ID>
                <Name>Bruce Paltrow</Name>
            </Student>
            <Student>
                <ID>4195655</ID>
                <Name>Santosh Gowswami</Name>
            </Student>
        </School>
    <Schools>
4

3 回答 3

3

我会创建单独School的和Student对象。您的解析器将具有currentSchool和的属性currentStudent。每当您的解析器命中<Student>标签时,调用

self.currentStudent = [[MyStudentObject alloc] init];

每当您的解析器命中</Student>标签时,调用

self.currentStudent = nil;

然后,当您点击<name>标签时,您可以检查是否有currentStudent. 如果你这样做了,那么这个名字就是那个学生的名字。如果没有当前学生,则名称为学校名称。

if (self.currentStudent)
{
    self.currentStudent.name = /*string between <name> tags*/
}
else
{
    self.currentSchool.name = /*string between <name> tags*/
}

抱歉,我的代码片段太短了,我现在没有太多时间输入。如果需要更多细节,我可以稍后添加更多代码。


更新

对我来说更详细的最快方法就是显示我正在寻找的代码,并将解释所有内容的注释放入代码中。如果对此有任何疑问,或者需要进一步解释,请告诉我要详细说明的内容,我会尽力而为。

学生XML.h

#import <Foundation/Foundation.h>

@interface StudentXML : NSObject

@property (nonatomic, strong) NSString *ID;     // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!!
@property (nonatomic, strong) NSString *Name;   // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!!

@end

学生XML.m

#import "StudentXML.h"

@implementation StudentXML

@end

SchoolXML.h

#import <Foundation/Foundation.h>
#import "StudentXML.h"

@interface SchoolXML : NSObject

@property (nonatomic, strong) NSString *ID;     // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!!
@property (nonatomic, strong) NSString *Name;   // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!!
@property (nonatomic, strong) NSMutableArray *studentsArray; // Array of StudentXML objects

@end

SchoolXML.m

#import "SchoolXML.h"

@implementation SchoolXML

// Need to overwrite init method so that array is created when new SchoolXML object is created
- (SchoolXML *) init;
{
    if (self = [super init])
    {
        self.studentsArray = [[NSMutableArray alloc] init];

        return self;
    }
    else
    {
        NSLog(@"Error - SchoolXML object could not be initialized in init on SchoolXML.m");
        return nil;
    }
}

@end

SchoolsParser.h

#import <Foundation/Foundation.h>
#import "SchoolXML.h"
#import "StudentXML.h"

@interface SchoolsParser : NSObject
{
    NSMutableString *currentElementValue;   // Will hold the string between tags until we decide where to put it
}

@property (nonatomic, strong) SchoolXML *currentSchool;     // Will hold the school that is in the process of being filled
@property (nonatomic, strong) StudentXML *currentStudent;   // Will hold the student that is in the process of being filled
@property (nonatomic, strong) NSMutableArray *allSchools;   // This is the final list of all the data in the XML file

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;

@end

学校解析器.m

#import "SchoolsParser.h"

@implementation SchoolsParser

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
// This method will be hit each time the parser sees an opening tag
// elementName is the string between the <> (example "School")
{
    if ([elementName isEqualToString:@"School"])
    {
        self.currentSchool = [[SchoolXML alloc] init];
    }
    else if ([elementName isEqualToString:@"Student"])
    {
        self.currentStudent = [[StudentXML alloc] init];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
// This method will be hit each time the parser sees a string between tags
// string is the value between the open and close tag (example "Fairfax High School")
// We take string and hold onto it until we can decide where it should be put
{
    currentElementValue = [[NSMutableString alloc] initWithString:string];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
// This method will be hit each time the parser sees an closing tag
// elementName is the string between the </> (example "School")
// This is the method where we decide where we want to put the currentElementValue string
{
    if ([elementName isEqualToString:@"Student"])
    {
        // Put the current student into the studentsArray of the currentSchool
        [self.currentSchool.studentsArray addObject:self.currentStudent];

        // We've finished building this student and have put it into the school we wanted, so we clear out currentStudent so we can reuse it next time
        self.currentStudent = nil;
    }
    else if ([elementName isEqualToString:@"School"])
    {
        // Put the current school into the allSchoolsArray to send back to our view controller
        [self.allSchools addObject:self.currentSchool];

        // We've finished building this school and have put it into the return array, so we clear out currentSchool so we can reuse it next time
        self.currentSchool = nil;
    }
    else if ([elementName isEqualToString:@"Schools"])
    {
        // We reached the end of the XML document
        return;
    }
    else
    // This is either a Name or an ID, so we want to put it into the correct currentSomething we are building
    {
        if (self.currentStudent)
        // There is a currentStudent, so the Name or ID we found is that of a student
        {
            // Since the properties of our currentStudent object exactly match the elementNames in our XML, the parser can automatically fills values in where they need to be without us doing any more
            // For example, it will take "Will Turner" in the <Name> tags in the XML and put it into the .Name property of our student
            [self.currentStudent setValue:currentElementValue forKey:elementName];
        }
        else
        // There was no student, so the Name or ID we found is that of a school
        {
            // Since the properties of our currentStudent object exactly match the elementNames in our XML, the parser can automatically fills values in where they need to be without us doing any more
            // For example, it will take "Fairfax High School" in the <Name> tags in the XML and put it into the .Name property of our school
            [self.currentSchool setValue:currentElementValue forKey:elementName];
        }
    }

    // We've now put the string in currentElementValue where we wanted it, so we clear out currentElementValue so we can reuse it next time
    currentElementValue = nil;
}

-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    NSLog(@"Error in SchoolsParser.m");
    NSLog(@"%@",parseError.description);
}

@end

您要开始解析的 UIViewController.m(确保您#include使用的是 SchoolXML、StudentXML 和 SchoolsParser):

- (void) startSchoolParser
{
    NSXMLParser *nsXmlParser = [[NSXMLParser alloc] initWithData:responseData]; // where "responseData" is the NSData object that holds your XML
    SchoolsParser *parser = [[SchoolsParser alloc] init];
    [nsXmlParser setDelegate:parser];
    if ([nsXmlParser parse])
    {
        // Parsing was successful
        NSArray *allSchools = parser.allSchools;
        // You can now loop through allSchools and use the data how ever you want

        // For example, this code just NSLog's all the data
        for (SchoolXML *school in allSchools)
        {
            NSLog(@"School Name = %@",school.Name);
            NSLog(@"School ID = %@",school.ID);
            for (StudentXML *student in school.studentsArray)
            {
                NSLog(@"Student Name = %@",student.Name);
                NSLog(@"Student ID = %@",student.ID);
            }
        }
    }
    else
    {
        NSLog(@"Parsing Failed");
    }
}
于 2013-05-09T13:47:35.023 回答
1

1) 当解析器遇到 School 标签时初始化一个数组,这个数组将保存您要创建的所有 Student 对象。

2)每次看到学生开始标签时创建学生对象。

3)然后解析器遇到ID标签解析成[studentObj setId:parsedContent]

4)然后解析器遇到名称标签[studentObj setName:parsedContent]

5) 现在解析器遇到学生标签的结尾,现在将此 studentObj 添加到您在步骤 1 中初始化的数组中

于 2013-05-09T13:24:45.850 回答
1

一种方法是只拥有两个BOOL类属性/ivars,一个用于学校,一个用于学生。然后在 中didStartElement,如果elementName@"School"@"Student",则设置适当的布尔属性/ivar。同样,在 中didEndElement,如果elementName@"School"@"Student",则清除相应的布尔属性/ivar。然后,在解析时@"Name",您可以检查这两个布尔属性/变量,以查看您正在解析哪一个并采取适当的步骤。(例如,如果学生布尔值是真的,那么显然这个名字是一个学生。如果学生布尔值是假的,但学校布尔值是真的,那么这个名字就是学校的名字。)

有更优雅的解决问题的方法(例如XML Node Parser),但这可能是最简单的。


顺便说一句,我不知道您是否对 XML 的结构有任何发言权,但我认为最好将所有数组都包装在它们自己的元素中。因此,而不是:

<Schools>
    <School>
        <ID>335823</ID>
        <Name>Fairfax High School</Name>
        <Student>
            <ID>4195653</ID>
            <Name>Will Turner</Name>
        </Student>
        <Student>
            <ID>4195654</ID>
            <Name>Bruce Paltrow</Name>
        </Student>
        <Student>
            <ID>4195655</ID>
            <Name>Santosh Gowswami</Name>
        </Student>
    </School>
<Schools>

我希望看到一个包含学生列表的“学生”元素名称。您的最后一个结束Schools标签也缺少/.

<Schools>
    <School>
        <ID>335823</ID>
        <Name>Fairfax High School</Name>
        <Students>
            <Student>
                <ID>4195653</ID>
                <Name>Will Turner</Name>
            </Student>
            <Student>
                <ID>4195654</ID>
                <Name>Bruce Paltrow</Name>
            </Student>
            <Student>
                <ID>4195655</ID>
                <Name>Santosh Gowswami</Name>
            </Student>
        </Students>
    </School>
</Schools>

您可以编写一个解析器来处理前者,但是当您进入一个动态生成结果的世界时,让 XML 更准确地反映底层数据的结构会很有用。我知道,当您正在为这个 XML 提要编写一个非常具体的解析器时,它可能看起来无关紧要,但它确实是一种更合乎逻辑的结构。

于 2013-05-09T13:57:18.787 回答