KissXML терпит неудачу с XML, содержащим инструкции обработки
Я столкнулся с проблемой, при которой XPath Queries не будет работать в KissXML, когда XML содержит инструкции по обработке, но будет работать нормально, если точно такой же XML не содержит каких-либо инструкций по обработке.
В качестве примера у меня есть следующий XML без инструкций по обработке (demo1.xml):
<?xml version="1.0"?>
<tst:TstForm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tst="http://schemas.somewhere.com/tst/1" xml:lang="en-US">
<tst:TstDetails>
<tst:Title>Labelling error on boxes</tst:Title>
<tst:Description>Box labels</tst:Description>
</tst:TstDetails>
</tst:TstForm>
Я анализирую XML и выполняю запрос XPath следующим образом:
// Parse the DEMO XML - no processing instructions,
// so this will work
NSLog(@"**** About to parse %@ ****\n\n", xmlFilename);
NSString *XMLPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:xmlFilename];
NSData *XMLData = [NSData dataWithContentsOfFile:XMLPath];
NSError *err=nil;
NSURL *furl = [NSURL fileURLWithPath:XMLPath];
if (!furl) {
NSLog(@"Can't create an URL from file: %@.", XMLPath);
return;
}
xmlViewDoc = [[DDXMLDocument alloc] initWithData:XMLData
options:0
error:&err];
// Prove that the XML was parsed correctly...
NSLog(@"Root node'%@' found\n\n", xmlViewDoc.rootElement.name);
NSLog(@"About to iterate through elements 2 levels down from the root node...");
NSArray *nodes = xmlViewDoc.rootElement.children;
for (DDXMLElement *node in nodes) {
NSArray *childNodes = node.children;
for (DDXMLElement *childNode in childNodes) {
NSLog(@" XPath: %@", childNode.XPath);
}
}
NSLog(@"Root node elements END\n\n");
// Show that the namespaces are parsed correctly
NSLog(@"Root node contains the following namespaces...");
nodes = xmlViewDoc.rootElement.namespaces;
for (DDXMLElement *node in nodes) {
NSLog(@" Found NS: '%@' = '%@'", node.name, node.stringValue);
}
NSLog(@"Namespaces END\n\n");
// Now execute an XPath query using the namespace
NSString *xPathQuery = @"/tst:TstForm/tst:TstDetails";
NSLog(@"Based on the above namespace and the XML structure, we should be able to execite the following XPath query:\n %@", xPathQuery);
NSLog(@"The XPath query should return two elements (Title & Description)...");
nodes = [xmlViewDoc.rootElement nodesForXPath:xPathQuery error:nil];
for (DDXMLElement *node in nodes) {
NSLog(@" XPath: %@", node.XPath);
}
NSLog(@"XPathQuery END\n\n");
Этот код обеспечивает следующий вывод журнала, как и ожидалось:
2012-09-03 13:04:25.662 NDPad[37359:c07] **** About to parse demo1.xml ****
2012-09-03 13:04:25.690 NDPad[37359:c07] Root node'tst:TstForm' found
2012-09-03 13:04:25.690 NDPad[37359:c07] About to iterate through elements 2 levels down from the root node...
2012-09-03 13:04:25.691 NDPad[37359:c07] XPath: /TstForm[1]/TstDetails[1]/Title[1]
2012-09-03 13:04:25.691 NDPad[37359:c07] XPath: /TstForm[1]/TstDetails[1]/Description[1]
2012-09-03 13:04:25.691 NDPad[37359:c07] Root node elements END
2012-09-03 13:04:25.691 NDPad[37359:c07] Root node contains the following namespaces...
2012-09-03 13:04:25.692 NDPad[37359:c07] Found NS: 'xsi' = 'http://www.w3.org/2001/XMLSchema-instance'
2012-09-03 13:04:25.692 NDPad[37359:c07] Found NS: 'tst' = 'http://schemas.somewhere.com/tst/1'
2012-09-03 13:04:25.692 NDPad[37359:c07] Namespaces END
2012-09-03 13:04:25.692 NDPad[37359:c07] Based on the above namespace and the XML structure, we should be able to execite the following XPath query:
/tst:TstForm/tst:TstDetails
2012-09-03 13:04:25.692 NDPad[37359:c07] The XPath query should return two elements (Title & Description)...
2012-09-03 13:04:25.693 NDPad[37359:c07] XPath: /TstForm[1]/TstDetails[1]
2012-09-03 13:04:25.693 NDPad[37359:c07] XPathQuery END
Однако, если я затем использую следующий XML, который содержит одну инструкцию обработки (demo1-5.xml):
<?xml version="1.0"?>
<?tst-example-instruction?>
<tst:TstForm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tst="http://schemas.somewhere.com/tst/1" xml:lang="en-US">
<tst:TstDetails>
<tst:Title>Labelling error on boxes</tst:Title>
<tst:Description>Box labels</tst:Description>
</tst:TstDetails>
</tst:TstForm>
Тот же код потерпит неудачу и предоставит только следующий вывод:
2012-09-03 13:04:25.693 NDPad[37359:c07] **** About to parse demo1-5.xml ****
2012-09-03 13:04:25.756 NDPad[37359:c07] Root node'tst:TstForm' found
2012-09-03 13:04:25.756 NDPad[37359:c07] About to iterate through elements 2 levels down from the root node...
2012-09-03 13:04:25.756 NDPad[37359:c07] XPath: /TstForm[1]/TstDetails[1]/Title[1]
2012-09-03 13:04:25.756 NDPad[37359:c07] XPath: /TstForm[1]/TstDetails[1]/Description[1]
2012-09-03 13:04:25.756 NDPad[37359:c07] Root node elements END
2012-09-03 13:04:25.757 NDPad[37359:c07] Root node contains the following namespaces...
2012-09-03 13:04:25.757 NDPad[37359:c07] Found NS: 'xsi' = 'http://www.w3.org/2001/XMLSchema-instance'
2012-09-03 13:04:25.757 NDPad[37359:c07] Found NS: 'tst' = 'http://schemas.somewhere.com/tst/1'
2012-09-03 13:04:25.757 NDPad[37359:c07] Namespaces END
2012-09-03 13:04:25.757 NDPad[37359:c07] Based on the above namespace and the XML structure, we should be able to execite the following XPath query:
/tst:TstForm/tst:TstDetails
2012-09-03 13:04:25.758 NDPad[37359:c07] The XPath query should return two elements (Title & Description)...
2012-09-03 13:04:25.758 NDPad[37359:c07] XPathQuery END
Я не вижу, что было бы не так с этим XML, что могло бы вызвать сбой запросов XPath, особенно когда итерация по DOM показывает, что он анализируется правильно.
Спасибо пол
1 ответ
Теперь у меня есть взлом, где я выполняю следующее:
- Прочитайте XML как строку.
- Разбор с использованием регулярного выражения, чтобы найти все инструкции по обработке.
- Добавьте все инструкции по обработке в массив строк.
- Используйте регулярное выражение, чтобы удалить инструкции обработки
Это позволяет мне без проблем выполнять запросы XPath. Однако, если мне нужно написать XML обратно, мне нужно:
- Создайте новую изменяемую строку для хранения выходного XML-документа /
- Переберите инструкции по обработке и добавьте их в новую строку выходного документа XML.
- Получить строку из моего реального XML-документа и добавить к новой выходной XML-строке.
Однако это далеко не оптимально, и я не понимаю, почему инструкции по обработке нарушают запросы XPath в KissXML. Вот пример моего кода:
// Load the XML file from the application bundle
NSString *xmlFilename = @"demo1-5.xml";
NSLog(@"**** About to parse %@ ****\n\n", xmlFilename);
NSString *XMLPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:xmlFilename];
NSString *xmlStringData = [NSString stringWithContentsOfFile:XMLPath encoding:NSUTF8StringEncoding error:nil];
// Now that we have the XML as a string, use regex to:
// 1) Find all processing instructions
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"<\\?.*?\\?>" options:NSRegularExpressionCaseInsensitive error:nil];
processingInstructions = [[NSMutableArray alloc] init];
[regex enumerateMatchesInString:xmlStringData options:0 range:NSMakeRange(0, [xmlStringData length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
// 2) Put all the matches in an array
NSString *toStore = [xmlStringData substringWithRange:[match range]];
[processingInstructions addObject:toStore];
}];
// 3) Remove all processing instructions from the XML string
NSString *newString = [regex stringByReplacingMatchesInString:xmlStringData options:0 range:NSMakeRange(0, xmlStringData.length) withTemplate:@""];
// Having removed the processing instructions, we can now parse the XML
NSError *err=nil;
NSURL *furl = [NSURL fileURLWithPath:XMLPath];
if (!furl) {
NSLog(@"Can't create an URL from file: %@.", XMLPath);
return;
}
xmlViewDoc = [[DDXMLDocument alloc] initWithXMLString:newString
options:0
error:&err];
// Now execute an XPath query using the namespace
NSString *xPathQuery = @"/tst:TstForm/tst:TstDetails";
NSArray *nodes = [xmlViewDoc.rootElement nodesForXPath:xPathQuery error:nil];
for (DDXMLElement *node in nodes) {
NSLog(@"Title Value: %@", node.stringValue);
}
// Finally we'll generate a new XML string which is a concatenation of
// the processing instructions and the NSXMLDocument.
NSMutableString *xmlOutputString = [[NSMutableString alloc] init];
for (NSString *piString in processingInstructions) {
[xmlOutputString appendFormat:@"%@\n", piString];
}
[xmlOutputString appendString:[xmlViewDoc XMLStringWithOptions:DDXMLNodePrettyPrint]];
NSLog(@"XML:\n%@", xmlOutputString);