2010-08-24 3 views
2

Je suis relativement nouveau dans l'analyse de fichiers XML et j'essaie de lire un gros fichier XML avec XMLReader.Comment lire un fichier XML avec un espace de noms indéfini avec XMLReader?

<?xml version="1.0" encoding="UTF-8"?> 
<ShowVehicleRemarketing environment="Production" lang="en-CA" release="8.1-Lite" xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd"> 
    <ApplicationArea> 
    <Sender> 
     <Component>Component</Component> 
     <Task>Task</Task> 
     <ReferenceId>w5/cron</ReferenceId> 
     <CreatorNameCode>CreatorNameCode</CreatorNameCode> 
     <SenderNameCode>SenderNameCode</SenderNameCode> 
     <SenderURI>http://www.example.com</SenderURI> 
     <Language>en-CA</Language> 
     <ServiceId>ServiceId</ServiceId> 
    </Sender> 
    <CreationDateTime>CreationDateTime</CreationDateTime> 
    <Destination> 
     <DestinationNameCode>example</DestinationNameCode> 
    </Destination> 
    </ApplicationArea> 
... 

Je RECEVOIR l'erreur suivante

ErrorException [Attention]: XMLReader :: read() [xmlreader.read]: compress.zlib: // D:/WebDev/exemple/local /public/../upload/example.xml.gz:2: erreur d'espace de noms: préfixe xsi pour namespace schemaLocation sur ShowVehicleRemarketing n'est pas défini

J'ai cherché partout et ne peut pas trouver beaucoup d'informations utiles sur l'utilisation XMLReader pour lire des fichiers XML avec des espaces de noms - Comment définir un nom rythme, si c'est en fait ce que je dois faire .. peu d'aide? liens vers des ressources pertinentes?

+1

duplication possible de [Comment lire un fichier XML qui a un espace de noms avec XMLReader?] (Http://stackoverflow.com/questions/3554724/how-to-read-an-xml-file-that-has- a-namespace-with-xmlreader) – VolkerK

+2

Bien que j'aime beaucoup mieux le titre de cette question que le précédent, c'est toujours un doublon. Pardon. – VolkerK

+0

Ce n'est même pas * juste * un doublon, c'est le même utilisateur qui pose à nouveau la même question, dans les deux heures .... – Abel

Répondre

5

Il doit y avoir une définition de l'espace de noms xsi. Par exemple.

<ShowVehicleRemarketing 
    environment="Production" 
    lang="en-CA" 
    release="8.1-Lite" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.starstandards.org/STAR/STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd" 
> 

Mise à jour: Vous pourriez write a user defined filter puis laisser le XMLReader use that filter, quelque chose comme:

stream_filter_register('darn', 'DarnFilter'); 
$src = 'php://filter/read=darn/resource=compress.zlib://something.xml.gz'; 
$reader->open($src); 

Le contenu lu par l'enveloppe de compress.zlib est alors "acheminé" à travers le DarnFilter qui doit trouver le (premier) emplacement où il peut insérer la déclaration xmlns: xsi. Mais cela est tout à fait désordre et prendra un certain se permettre de le faire (par exemple du godet théoriquement pourrait contenir xs, seau B i:schem et seau C aLocation=")


Mise à jour 2: voici un exemple ad hoc d'un filtre dans php qui insère la déclaration d'espace de noms xsi. La plupart du temps non testé (a travaillé avec le test que j'ai couru ;-)) et non documenté. Prenez-le comme preuve de concept et non comme code de production.

<?php 
stream_filter_register('darn', 'DarnFilter'); 
$src = 'php://filter/read=darn/resource=compress.zlib://d:/test.xml.gz'; 

$r = new XMLReader; 
$r->open($src); 
while($r->read()) { 
    echo '.'; 
} 

class DarnFilter extends php_user_filter { 
    protected $buffer=''; 
    protected $status = PSFS_FEED_ME; 

    public function filter($in, $out, &$consumed, $closing) 
    { 
    while ($bucket = stream_bucket_make_writeable($in)) { 
     $consumed += $bucket->datalen; 
     if (PSFS_PASS_ON == $this->status) { 
     // we're already done, just copy the content 
     stream_bucket_append($out, $bucket); 
     } 
     else { 
     $this->buffer .= $bucket->data; 
     if ($this->foo()) { 
      // first element found 
      // send the current buffer   
      $bucket->data = $this->buffer; 
      $bucket->datalen = strlen($bucket->data); 
      stream_bucket_append($out, $bucket); 
      $this->buffer = null; 
      // no need for further processing 
      $this->status = PSFS_PASS_ON; 
     } 
     } 
    } 
    return $this->status; 
    } 

    /* looks for the first (root) element in $this->buffer 
    * if it doesn't contain a xsi namespace decl inserts it 
    */ 
    protected function foo() { 
    $rc = false; 
    if (preg_match('!<([^?>\s]+)\s?([^>]*)>!', $this->buffer, $m, PREG_OFFSET_CAPTURE)) { 
     $rc = true; 
     if (false===strpos($m[2][0], 'xmlns:xsi')) { 
     echo ' inserting xsi decl '; 
     $in = '<'.$m[1][0] 
      . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' 
      . $m[2][0] . '>';  
     $this->buffer = substr($this->buffer, 0, $m[0][1]) 
      . $in 
      . substr($this->buffer, $m[0][1] + strlen($m[0][0])); 
     } 
    } 
    return $rc; 
    } 
} 

Mise à jour 3: Et voici une solution ad hoc écrit en C#

XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); 
// prime the XMLReader with the xsi namespace 
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); 

using (XmlReader reader = XmlTextReader.Create(
    new GZipStream(new FileStream(@"\test.xml.gz", FileMode.Open, FileAccess.Read), CompressionMode.Decompress), 
    new XmlReaderSettings(), 
    new XmlParserContext(null, nsmgr, null, XmlSpace.None) 
)) { 
    while (reader.Read()) 
    { 
    System.Console.Write('.'); 
    } 
} 
+0

Okay ... disons que le XML est distant et que je ne peux pas le changer - est-ce qu'il y a un moyen d'ignorer le fait que le document semble malformé, c'est-à-dire qu'il manque une définition d'espace de noms? – JeremyFelix

+0

Je ne pense pas que XMLReader de php ait une option pour ignorer ce genre d'erreur ou un moyen d '"injecter" une déclaration d'espace de noms. On dirait que vous devez modifier les documents, peut-être à la volée, mais cela ne va pas vraiment stimuler la performance. PHP est votre seule option? Par exemple. le dotnet XMLReader peut être initialisé avec un XmlParserContext qui contient déjà des espaces de noms prédéfinis. http://msdn.microsoft.com/fr-fr/library/xc8bact5.aspx – VolkerK

+0

PHP est la seule option - existe-t-il un moyen, pensez-vous, de modifier le document avant d'essayer de le lire sans charger le document? chose entière en mémoire? Un couple d'autres complications - Il est gzippé et ~ 300Mb non compressé .. Les choses commencent à l'air compliqué/sans espoir – JeremyFelix

1

Vous pouvez file_get_contents et str_replace XML avant de passer à XMLReader.

Soit insérer le declararation d'espace de noms requis pour le préfixe xsi:

$reader = new XMLReader; 
$reader->xml(str_replace(
    '<ShowVehicleRemarketing', 
    '<ShowVehicleRemarketing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"', 
    file_get_contents('http://example.com/data.xml'))); 

Une autre option serait de supprimer l'attribut schemaLocation:

$reader->xml(str_replace(
    'xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd"', 
    '', 
    file_get_contents('http://example.com/data.xml'))); 

Cependant, s'il y a plusieurs préfixes dans le document, vous devrez tous les remplacer.

+0

* sigh * Cela fonctionnerait bien si le fichier n'était pas ~ 300Mb Peut-être que je devrais explorer une option pour essayer de réécrire sans charger le fichier entier dans la mémoire? – JeremyFelix

+0

@Fixix hmm, je n'ai jamais essayé ça, mais vous pourriez utiliser les [fonctions libxml] (http://de.php.net/manual/fr/function.libxml-set-streams-context.php) pour enregistrer un filtre de flux personnalisé qui modifie les données avant qu'elles ne soient traitées par XmlReader. – Gordon

0

Réparer tout ce qui est en train d'écrire du code XML mal formé, ou écrire un outil séparé pour effectuer le correctif plus tard. (Il n'a pas à tout lire en même temps dans la mémoire, nécessairement - diffuser les données en entrée/sortie, peut-être lire et écrire une ligne à la fois.De cette façon, votre code de lecture n'a pas à s'inquiéter d'essayer de faire quelque chose d'utile avec les données et en le réparant en même temps.

1

L'espace de noms xsi est normalement réservé pour une utilisation avec Schema Instance Namespace:

xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 

si ce n'est pas, votre fichier XML n'est pas XML + NS conforme et ne peut pas être analysé. Donc, vous devriez résoudre cela dans le document source.

Une note sur xsi: elle est encore plus essentielle que d'autres espaces de noms possibles, car elle dirige un analyseur de validation vers les emplacements de schéma corrects pour le schéma de votre XML.

Questions connexes