使用NSXMLparser,你如何解析具有相同名称标签但在不同元素中的xml文件?
是否有可能在NSXMLparser中提供路径表达式?我有一个XML文件,它具有几个相同的名称标签,但它们位于不同的元素中。有什么办法可以区分它们吗?这里是XML:使用NSXMLparser,你如何解析具有相同名称标签但在不同元素中的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>
我会创建单独的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*/
}
对不起我的代码片段是如此短暂,我没有太多的时间,现在键入此。如果需要更多细节,我可以稍后添加更多代码。
UPDATE
对我来说,进入更详细的最快方法是只是为了显示我正在寻找的代码,并把一切都解释成代码中的注释。如果对此有任何疑问,或者需要进一步解释,请告诉我要详细说明的内容,我会尽我所能。
StudentXML.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
StudentXML.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
SchoolsParser。米
#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");
}
}
请详细说明,因为我不明白我如何在单个NSDictionnary中添加所有元素。 – 2013-05-09 13:59:52
@ user2299789:您可以将NSDictionaries嵌套到另一个中,因此如果这是您需要的,您总是可以最终生成一个包含所有内容的最终NSDictionary。你可以使用解析器的.m文件中的代码更新你的问题吗?我想看看你是如何使用解析器委托方法。 – GeneralMike 2013-05-09 14:07:07
是的,这是我的问题,但直到现在我没有写解析器。文件,因为我没有找到解决方案。 – 2013-05-09 15:44:04
1)当解析器遇到School标签时初始化一个数组,这个数组将保存你要创建的所有Student对象。
2)每次看到学生开始标签时创建学生对象。
3)然后分析器遇到ID标签解析成[studentObj SETID:parsedContent]
4)然后分析器遇到名称标记[studentObj的setName:parsedContent]
5)现在分析器遇到学生标签的端,现在将此studentObj添加到您在步骤1中初始化的数组中
一种方法是只具有两个BOOL
类属性/高德,一个针对学校,另一个针对学生。然后在didStartElement
中,如果elementName
是@"School"
或@"Student"
,那么设置合适的布尔属性/ ivar。同样,在didEndElement
中,如果elementName
是@"School"
或@"Student"
,则清除相应的布尔属性/ ivar。然后,在解析@"Name"
时,可以检查这两个布尔属性/ ivars以查看您正在解析哪一个属性并采取适当的步骤。 (例如,如果学生的布尔值为true,那么显然这个名字是学生,如果学生布尔值为假,但学校布尔值为true,那么名称就是学校的名称。)
还有更多解决问题的优雅方法(例如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 feed编写一个非常具体的解析器时,它可能看起来并不相关,但它确实是一个更合理的结构。
是的,用这种方法,我可以添加所有elemebt在一个NSDictionnary? – 2013-05-09 14:03:50
有了这个设置,你真的只需要1个'BOOL',对于学生来说,对吧?假设名称不是学生的名字,它必须是学校的名字。 – GeneralMike 2013-05-09 14:10:04
@GeneralMike - 显然,是的,你只需要这个XML的一个布尔值。 (事实上,当你有你的结构来解析和填充你的树的节点时,你不需要任何)。我只是想让它变得非常明显而且非常简单。并且,通过使用两个布尔值,您可以更强大一些,能够检测格式不正确的XML,并且能够处理如果'Schools'标签后来嵌入到具有自己名称的“区域”结构中(例如,人们经常给我们简化格式的XML)等。 – Rob 2013-05-09 14:12:27
您的XML在最后的结束标记上也缺少'/'。 – Rob 2013-05-09 15:42:15