2010-09-11 8 views
2

J'essaie d'interroger XML via XPATH mais je n'arrive pas à faire fonctionner id(). Je voudrais obtenir tous les auteurs pour un livre, en spécifiant l'ID du livre.id() dans XPATH avec .NET

Voici le code XML.

<?xml version="1.0" encoding="utf-8" ?> 
<bookstore xmlns="http://litemedia.se/BookStore.xsd"> 
    <book id="ISBN9789170375033"> 
    <title>I väntan på talibanerna</title> 
    <cover>http://image.bokus.com/images2/9789170375033_large</cover> 
    <author-ref id="A1" /> 
    </book> 

    <book id="ISBN9789170372063"> 
    <title>Sista resan till Phnom Penh</title> 
    <cover>http://image.bokus.com/images2/9789170372063_large</cover> 
    <author-ref id="A1" /> 
    </book> 

    <book id="ISBN9789127121867"> 
    <title>Vårt bröllop : Kronprinsessan Victoria och Prins Daniel 19 juni 2010</title> 
    <cover>http://image.bokus.com/images2/9789127121867_large</cover> 
    <author-ref id="A2 A3" />  
    </book> 

    <book id="ISBN9789189204966"> 
    <title>Människa, människa</title> 
    <cover>http://image.bokus.com/images2/9789189204966_large</cover> 
    <author-ref id="A3" /> 
    </book> 

    <author id="A1"> 
    <name>Jesper Huor</name> 
    </author> 
    <author id="A2"> 
    <name>Susanna Popova</name> 
    </author> 
    <author id="A3"> 
    <name>Paul Hansen</name> 
    </author> 
</bookstore> 

Et ceci est mon schéma.

<?xml version="1.0" encoding="utf-8"?> 
<xs:schema id="BookStore" 
    targetNamespace="http://litemedia.se/BookStore.xsd" 
    elementFormDefault="qualified" 
    xmlns="http://litemedia.se/BookStore.xsd" 
    xmlns:mstns="http://litemedia.se/BookStore.xsd" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
> 
    <xs:element name="bookstore"> 
    <xs:complexType mixed="true"> 
     <xs:sequence> 
     <xs:element name="book" maxOccurs="unbounded"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="title" type="xs:string" /> 
       <xs:element name="cover" type="xs:anyURI" /> 
       <xs:element name="author-ref"> 
       <xs:complexType> 
        <xs:attribute name="id" type="xs:IDREFS"/> 
       </xs:complexType> 
       </xs:element> 
      </xs:sequence> 

      <xs:attribute name="id" type="xs:ID" /> 
      </xs:complexType> 
     </xs:element> 

     <xs:element name="author" maxOccurs="unbounded"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="name" type="xs:string" /> 
      </xs:sequence> 

      <xs:attribute name="id" type="xs:ID" /> 
      </xs:complexType> 
     </xs:element> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 
</xs:schema> 

Ceci est mon code.

public IList<Author> GetAuthorsForBook(string isbn) 
{ 
    using (var xmlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(XmlDataSourcePath)) 
    using (var xsdStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(XsdDataSourcePath)) 
    { 
     var doc = new XmlDocument(); 

     // Load schema 
     var schema = XmlSchema.Read(xsdStream, SchemaEvents); 
     doc.Schemas.Add(schema); 

     // Load document 
     doc.Load(xmlStream); 

     // Load default namespace: bs 
     var nsmgr = new XmlNamespaceManager(doc.NameTable); 
     nsmgr.AddNamespace("bs", "http://litemedia.se/BookStore.xsd"); 

     // We should be able to do this 
     // var path = string.Format("id(id('{0}')/bs:author-ref/@id)", isbn); 

     // but this will have to do 
     var path = string.Format("/bs:bookstore/bs:author/@id[contains(/bs:bookstore/bs:book[@id='{0}']/bs:author-ref/@id,.)]", isbn); 

     return doc.SelectNodes(path, nsmgr).Cast<XmlNode>() 
      .Select(node => 
      new Author 
      { 
       Name = node.FirstChild.Value 
      }).ToList(); 
    } 
} 

Depuis que j'ai spécifié BookID et AuthorID que le type d'identification dans le schéma que je voudrais être en mesure de faire ce qui suit.

var path = string.Format("id(id('{0}')/bs:author-ref/@id)", isbn); 

Cette requête renvoie toujours 0 élément. Si je le réduis en id ('ISBN9789127121867') cela retournera aussi 0 résultat, ce qui indique que id() ne fonctionne pas dans mon scénario. :(

Au moment où je vais pour la requête suivante, même si ce n'est pas aussi efficace que id() serait, il ne me donne les résultats dont j'ai besoin.

var path = string.Format("/bs:bookstore/bs:author/@id[contains(/bs:bookstore/bs:book[@id='{0}']/bs:author-ref/@id,.)]", isbn); 

Avez-vous jamais eu un problème similaire ou tout indice de ce que je pourrais avoir fait de mal? Merci!

Mikael Lundin

+0

Mikael, la mise en oeuvre XSLT Microsoft .NET Framework et XPath support XSLT et XPath 1.0 et XPath 1.0 est antérieure du langage de schéma XML du W3C donc tout ce qui est défini dans http://www.w3.org/TR/xpath/# unique-id est un identifiant basé sur la DTD. Vous semblez vous attendre à ce que les ID définis par votre schéma aient un impact sur la fonction d'ID de XPath 1.0, mais ce n'est pas le cas. Si vous souhaitez que l'ID basé sur un schéma fonctionne avec XPath et XSLT, vous devez utiliser une implémentation XPath 2.0 compatible avec le schéma, telle que Saxon ou XQSharp. –

Répondre

2

Comme expliqué dans mon commentaire, ids défini de schéma n'importe XPath 1.0. Pour vous donner un exemple de fonctionnement avec un XPath 2 prenant en charge les schémas.0 mise en œuvre, voici un code à l'aide des méthodes d'extension XQSharp sur XMLNodes:

XmlDocument doc = new XmlDocument(); 
    XmlReaderSettings xrs = new XmlReaderSettings() 
    { 
     ValidationType = ValidationType.Schema 
    }; 
    xrs.Schemas.Add(null, "schema.xsd"); 
    using (XmlReader xr = XmlReader.Create("input.xml", xrs)) 
    { 
     doc.Load(xr); 
    } 

    string isbn = "ISBN9789127121867"; 

    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); 
    nsmgr.AddNamespace("bs", "http://litemedia.se/BookStore.xsd"); 

    string path = string.Format("id(id('{0}')/bs:author-ref/@id)", isbn); 
    foreach (XmlElement author in doc.XPathSelectNodes(path, nsmgr)) 
    { 
     Console.WriteLine(author.OuterXml); 
    } 

Quand je lance que dans un projet où j'ai ajouté des références à XQSharp et XQSharp.ExtensionMethods et ont ajouté un using XQSharp.ExtensionMethods; il émet les deux auteurs éléments comme par exemple

<author id="A2" xmlns="http://litemedia.se/BookStore.xsd"><name>Susanna Popova</name></author> 
<author id="A3" xmlns="http://litemedia.se/BookStore.xsd"><name>Paul Hansen</name></author> 
+0

+1 pour une bonne explication. –

+0

Je vois, merci pour votre réponse détaillée. Je ne connaissais pas le projet XQSharp. Cela vaut la peine de regarder de plus près. –

0

IIRC (depuis longtemps en arrière) le traitement id travaille pour xdr mais pas xsd.

Peut-être juste utiliser // * [@ id = 'foo']

1

la fonction XPath AFAIK id() est pas mis en œuvre XslCompiledTransform.

Par exemple:

XSLT stylesheet:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/"> 
    <xsl:copy-of select="id('a22')"/> 
</xsl:template> 
</xsl:stylesheet> 

document XML Source:

<!DOCTYPE test [ 
    <!ELEMENT test (x+)> 
    <!ELEMENT x (x+| y+)> 
    <!ATTLIST x 
    a ID #REQUIRED> 
    <!ELEMENT y ANY> 
]> 
<test> 
    <x a="a11"> 
     <x a="a21"> 
     <x a="a31"> 
      <y>y31</y> 
      <y>y32</y> 
     </x> 
     </x> 
    </x> 
    <x a="a12"> 
     <x a="a22"> 
     <y>y21</y> 
     <y>y22</y> 
     </x> 
    </x> 
    <x a="a13"> 
     <y>y11</y> 
     <y>y12</y> 
    </x> 
    <x a="a14"> 
     <y>y03</y> 
     <y>y04</y> 
    </x> 
</test> 

Résultats avec MSXML3/4:

<x a="a22"> 
     <y>y21</y> 
     <y>y22</y> 
     </x> 

Résultat avec XslCompiledTransform ou XslTransform:

(Nothing) 
+0

Dimitre, comment avez-vous exécuté votre feuille de style? J'ai simplement ajouté vos deux exemples, le fichier XML et la feuille de style, à un projet VS.NET 3.5 C#, associé la feuille de style au XML dans l'EDI, puis utilisé le bouton "Afficher sortie XSLT" et le résultat de la transformation élément "x" avec l'attribut a = "a22". –

+0

@ Martin-Honnen: J'utilise nxslt2.exe. –