1

XML に処理命令が含まれている場合、KissXML で XPath クエリが失敗するという問題が発生しましたが、まったく同じ XML に処理命令が含まれていない場合は問題なく動作します。

例として、処理命令 (demo1.xml) を含まない次の 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

ただし、単一の処理命令 (demo1-5.xml) を含む次の 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 を反復処理して、正しく解析されたことが示されている場合はそうです。

ありがとう、ポール

4

1 に答える 1

1

私は今、次のことを実行するハックを持っています:

  • XMLを文字列として読み取ります。
  • 正規表現を使用して解析し、すべての処理命令を見つけます。
  • すべての処理命令を文字列配列に追加します。
  • 正規表現を使用して処理命令を削除します

これにより、XPathクエリを問題なく実行できるようになります。ただし、XMLを書き戻す必要がある場合は、次のことを行う必要があります。

  • 出力XMLドキュメントを保持するための新しい可変文字列を作成します/
  • 処理命令をループして、新しい出力XMLドキュメント文字列に追加します。
  • 実際のXMLドキュメントから文字列を取得し、新しい出力XML文字列に追加します。

ただし、これは最適とはほど遠いため、処理命令がKissXMLのXPathクエリを壊す理由がわかりません。これが私のコードの例です:

    // 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);
于 2012-09-03T23:18:32.307 に答える