我有一个xml文件,如下所示。我正在使用NSXMLParser,但是我无法解析我的作者和摘要。由于访问权限,我无法编辑xml文件。
任何解决方案?
XML文件:
<book>
<title>Book 1</title>
<author>
<subfield id="a"> Jason </subfield>
<subfield id="b"> Alfonso. </subfield>
</author>
<summary>
<subfield id="a"> Milano </subfield>
<subfield id="b"> Italy </subfield>
</summary>
</book>
我的代码:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
attributes = [attributeDict copy];
if ([elementName isEqualToString:@"book"]) {
item = [[NSMutableDictionary alloc] init];
} else if ([elementName isEqualToString:@"title"]) {
self.title = [[NSMutableString alloc] init];
} else if ([elementName isEqualToString:@"subfield"]) {
if ([[attributeDict valueForKey:@"id"] isEqualToString:@"a"]) {
self.authorName1 = [[NSMutableString alloc] init];
}
} else if ([elementName isEqualToString:@"subfield"]) {
if ([[attributeDict valueForKey:@"id"] isEqualToString:@"b"]) {
self.authorName2 = [[NSMutableString alloc] init];
}
}
}
我能抓住子场。然而,甚至摘要的子字段是在作者期间抓取的,我不知道该怎么做。我需要两者分开
答案 0 :(得分:4)
请注意,可能更容易将您的应用基于Core Data或NSXML(使用适当的init method of NSXMLDocument
),而不是负责解析XML文件。如果您希望这样做,请继续阅读。
为子字段元素使用不同的元素名称将解决clobbering问题。
<book>
<title>Book 1</title>
<author>
<first> Jason </first>
<last> Alfonso. </last>
</author>
<summary>
<city> Milano </city>
<country> Italy </country>
</summary>
</book>
然而,还有更好的方法。
一般来说,要正确解析XML文件,您需要在进程中维护一堆元素。解析元素时,您将创建一个新元素并将其添加到堆栈中。解析元素完成后,从堆栈中弹出一个元素并将其提供给堆栈顶部的元素。您可以使用工厂方法(下面的-nodeWithTag:attributes:parser:
)和将元素名称映射到类(下面是elementClasses
)的字典,根据元素名称创建不同类的元素。
/* category to return a default object (rather than nil)
when a key isn't present in a dictionary.
*/
@interface NSDictionary (defaultObject)
-(id)objectForKey:(NSString*)key default:(id)default;
@end
@implementation NSDictionary (defaultObject)
-(id)objectForKey:(NSString*)key default:(id)default {
id object = [self objectForKey:key];
if (nil == object) {
return default;
}
return object;
}
@end
/* category to add aliases for stack operations
to NSMutableArray
*/
@interface NSMutableArray (stack)
-(void)push:(id)object;
-(id)pop;
-(id)top;
@end
@implementation NSMutableArray (stack)
// could also use class_addMethod to alias push & top
-(void)push:(id)object {
[self addObject:object];
}
-(id)pop {
id last = [self lastObject];
[self removeLastObject];
return last;
}
-(id)top {
return [self lastObject];
}
@end
// the parser delegate.
@interface ... <NSXMLParserDelegate> {
NSMutableArray activeElements;
id item;
...
@property (nonatomic,retain) item;
@end
@implementation ...
@synthesize item;
#pragma mark Class members
// map element names to classes
static NSDictionary *elementClasses;
+(void)initialize {
nodeTypes=[[NSDictionary alloc] initWithObjectsAndKeys:
// Just an illustrative example of a custom class.
// You don't necessarily need a Book class.
[Book class],@"book",
nil];
}
// if you have other init methods, make sure activeElements is created.
-(id)init {
if ((self = [super init])) {
activeElements = [[NSMutableArray alloc] init];
...
}
return self;
}
-(void)parserDidStartDocument:(NSXMLParser *)parser {
// add sentinel element so stack isn't empty at start.
[activeElements push:[self nodeWithTag:@"root" attributes:nil parser:parser]];
}
-(void)parserDidEndDocument:(NSXMLParser *)parser {
// The parser should ensure only case 1 is reachable, but still...
switch ([activeElements count]) {
case 0:
NSLog(@"Root element removed from stack early.");
break;
default:
NSLog(@"Extra elements in stack at parse end.");
[activeElements removeObjectsInRange:NSMakeRange(1, activeElements.count-1)];
// FALLTHRU
case 1:
// top item should be the sentinel
self.item = [activeElements pop];
if ([item.children count] == 1) {
// sentinel can safely be discarded if
self.item = [item.children objectAtIndex:0];
}
break;
}
}
#pragma mark Instance methods
-(void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
[activeElements push:[self nodeWithTag:elementName
attributes:attributeDict
parser:parser]];
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
id element = [activeElements pop];
if (element.attributes.count == 0 && element.children.count == 0) {
// simple leafs don't need to be Nodes.
[activeElements.top setValue:element.value forKey:elementName];
} else {
[activeElements.top setValue:element forKey:elementName];
}
}
-(void)parser:(NSXMLParser*)parser foundCharacters:(NSString*)string {
activeElements.top.value = string;
}
/* Factory method. Depending on elementName, create an
object of the appropriate type.
*/
-(id)nodeWithTag:elementName attributes:attrs parser:(NSXMLParser*)parser {
id node =[[[elementClasses objectForKey:elementName
default:[NSMutableDictionary class]]
alloc] init];
for (id key in attrs) {
@try {
[node setValue:[attrs objectForKey:key] forKey:key];
}
@catch (NSException *exc) {
// TODO: warn user of invalid attribute(s) when parsing is finished
if ([exc name] == NSUndefinedKeyException) {
NSLog(@"%d,%d: Set attribute '%@' on a %@, but it doesn't have that property.",
[parser columnNumber], [parser lineNumber],
key, elementName);
} else {
NSLog(@"%d,%d: Caught %@ when setting %@ on a %@.",
[parser columnNumber], [parser lineNumber],
[exc name], key, elementName);
}
}
}
return [node autorelease];
}
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(@"parse error: %@", parseError);
[self abort];
}
-(void)parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validError {
NSLog(@"validation error: %@", validError);
[self abort];
}
-(void)abort {
[activeElements removeAllObjects];
}
仔细看看:
} else if ([elementName isEqualToString:@"subfield"]) {
if ([[attributeDict valueForKey:@"id"] isEqualToString:@"a"]) {
self.authorName1 = [[NSMutableString alloc] init];
}
} else if ([elementName isEqualToString:@"subfield"]) {
if ([[attributeDict valueForKey:@"id"] isEqualToString:@"b"]) {
self.authorName2 = [[NSMutableString alloc] init];
}
}
如果第一次测试成功,你将永远不会达到第二次。这里简单的解决方法是组合块,尽管这仍然会有问题:
} else if ([elementName isEqualToString:@"subfield"]) {
if ([[attributeDict valueForKey:@"id"] isEqualToString:@"a"]) {
self.authorName1 = [[NSMutableString alloc] init];
} else if ([[attributeDict valueForKey:@"id"] isEqualToString:@"b"]) {
self.authorName2 = [[NSMutableString alloc] init];
}
}
答案 1 :(得分:2)
下载: https://github.com/Insert-Witty-Name/XML-to-NSDictionary
然后你只需:
NSDictionary *dic = [XMLReader dictionaryForPath:filepath error:nil];
结果是一个NSDictionary * dic,其中包含字典,数组和字符串,具体取决于XML:
{
book = {
author = {
first = Jason;
last = "Alfonso.";
};
summary = {
city = Milano;
country = Italy;
};
title = "Book 1";
};
}