2010-03-25 9 views
1

Après avoir converti un fichier XML fautif en utilisant regex, j'ai maintenant besoin de le changer encore une fois. Ce fichier sourceConvertir le fichier XML en CSV

<product> 
    <sku>SP00001</sku> 
    <PID_OWNER_SellerID>StoreName</PID_OWNER_SellerID> 
    <EANCode>8711983489813</EANCode> 
    <DeliveryDays>2</DeliveryDays> 
</product> 

doit devenir un fichier CSV, mais comme celui-ci:

sku  field     value 
SP00001 PID_OWNER_SellerID StoreName 
SP00001 EANCode    8711983489813 
SP00001 DeliveryDays   2 

Je suppose que c'est en dehors du champ d'application « regex et doit être fait avec XSL?

+0

Vous cherchez un code pour résoudre ce problème? Ou, un outil existant pour convertir xml en csv? – geffchang

+0

Découvrez cette question: http://stackoverflow.com/questions/365312/xml-to-csv-using-xslt – gooch

+1

Quel (s) langage (s) de programmation voulez-vous ou avez-vous? Cela peut être fait via XSL, ou vous pouvez utiliser .NET ou d'autres langages pour cela. –

Répondre

4

Voici quelques XSLT pour vous ...

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
       exclude-result-prefixes="msxsl" 
    > 
    <xsl:output method="text" indent="yes"/> 

    <xsl:template match="/"> 
    <xsl:call-template name="headerRow" /> 
    <xsl:apply-templates select="//product" /> 
    </xsl:template> 

    <xsl:template name="headerRow"> 
    <xsl:call-template name="rightpad"> 
     <xsl:with-param name="fieldvalue" select="'sku'"/> 
     <xsl:with-param name="fieldsize" select="number(11)"/> 
    </xsl:call-template> 

    <xsl:call-template name="rightpad"> 
     <xsl:with-param name="fieldvalue" select="'field'"/> 
     <xsl:with-param name="fieldsize" select="number(22)"/> 
    </xsl:call-template> 

    <xsl:text>value&#xd;&#xa;</xsl:text> 
    </xsl:template> 

    <xsl:template match="product"> 
    <xsl:for-each select="node()[local-name(.) != 'sku']"> 

     <xsl:call-template name="rightpad"> 
     <xsl:with-param name="fieldvalue" select="../sku"/> 
     <xsl:with-param name="fieldsize" select="number(11)"/> 
     </xsl:call-template> 

     <xsl:call-template name="rightpad"> 
     <xsl:with-param name="fieldvalue" select="local-name(.)"/> 
     <xsl:with-param name="fieldsize" select="number(22)"/> 
     </xsl:call-template> 

     <xsl:value-of select="."/> 
     <xsl:text>&#xd;&#xa;</xsl:text> 
    </xsl:for-each> 
    </xsl:template> 

    <xsl:template name="rightpad"> 
    <xsl:param name="fieldvalue" select="string('')"/> 
    <xsl:param name="fieldsize" select="0"/> 

    <xsl:variable name="padded" 
        select="concat($fieldvalue, '     ')" /> 
    <xsl:variable name="result" 
        select="substring($padded,1,$fieldsize)" /> 

    <xsl:value-of select="$result"/> 
    </xsl:template> 

</xsl:stylesheet> 
+0

il existe de meilleurs moyens de former ce XSLT. Mais c'est à peu près le plus court. J'ai un exemple plus long qui fonctionnera automatiquement si vous ajoutez plus d'éléments. Mais cette feuille de style est à peu près aussi longue que toute la réponse ci-dessus. –

+0

Je suis très intéressé par cet exemple, car j'ai en fait environ 84 types d'éléments différents dans le XML. – skerit

+0

Je l'afficherai sur mon blog ce week-end (je ne veux pas vraiment spammer cette réponse.) Mais l'idée de base est que j'ai trouvé un modèle de remplissage pour gérer la largeur fixe puis j'ai écrit un modèle récursif pour interroger chaque produit et boucle les éléments enfants avec le nom local (.) ... Difficile de décrire ce qui varie pratique à utiliser. –

6

C'est généralement une mauvaise idée de tenter d'analyser XML en utilisant des expressions régulières, car il y a une façon infinie de formater un document XML qui est structurellement le même, et qui va pourtant embobiner vos expressions régulières.

Pour les fichiers qui ne sont pas massifs, utilisez définitivement XSL, en n'oubliant pas de spécifier "text" comme méthode de sortie. N'oubliez pas que vous pouvez appeler un processus XSL par programmation si vous le devez - la plupart des langues vous le permettent.

Pour les fichiers volumineux, pensez à écrire un petit programme utilisant une API de diffusion en continu (par exemple SAX ou l'une des API push-parser).

1
XPathDocumemt x = new XPathDocument("yourdoc.xml");  
XPathNavigator n = x.CreateNavigator();  
XPathNodeIterator i = n.Select("root/product"); 

List<string> fields = new List<string>() { "PID_OWNER_SellerID", "EANCode", "DeliveryDays" } 

using (TextWriter w = File.CreateText("c:\\yourfile.csv")) 
{ 
    w.WriteLine("sku, field, value"); 

    while (i.MoveNext()) 
    { 
     foreach (string field in fields) 
     { 
      w.WriteLine(string.Format("{0}, {1}, {2}", i.Current.SelectSingleNode("sku").value, field, i.Current.selectSingleNode(field).Value)); 
     } 
    } 
} 
1

Cette stylesheet produira la sortie dans le format spécifié:

<?xml version="1.0" encoding="UTF-8"?> 
    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes"/> 

    <!--Spacing between column1 and column2 is 11 characters--> 
    <xsl:variable name="col1-spaces" select="'   '" /> 
    <!--Spacing between column2 and column3 is 22 characters--> 
    <xsl:variable name="col2-spaces" select="concat($col1-spaces, $col1-spaces)" /> 

    <xsl:template match="/"> 
    <!--Generate the heading row first, then apply templates--> 
    <xsl:text>sku</xsl:text> 
    <!--Add enough spaces after to align the next column--> 
    <xsl:value-of select="substring($col1-spaces, 3)"/> 
    <xsl:text>field</xsl:text> 
    <!--Add enough spaces after to align the next column--> 
    <xsl:value-of select="substring($col2-spaces, 5)"/> 
    <xsl:text>value&#10;</xsl:text> 
    <xsl:apply-templates /> 
    </xsl:template> 

    <!--Do nothing with sku elements--> 
    <xsl:template match="sku" /> 

    <!--For all elements that are children of product, except for sku, do this--> 
    <xsl:template match="product/*[not(self::sku)]"> 
     <xsl:value-of select="preceding-sibling::sku"/> 
     <!--Calculate how many spaces are needed using the length of the value of sku --> 
     <xsl:value-of select="substring($col1-separator, string-length(preceding-sibling::sku))"/> 
     <xsl:value-of select="local-name()" /> 
     <!--Calculate how many spaces are needed using the length of the name of the current element--> 
     <xsl:value-of select="substring($col2-separator, string-length(local-name()))"/> 
     <xsl:value-of select="." /> 
     <xsl:text>&#10;</xsl:text> 
    </xsl:template> 

</xsl:stylesheet> 
+0

C'est ce dont j'ai besoin, bien que certains noms de champs soient assez grands. Lorsque j'essaie d'utiliser ceci dans le plugin firefox "XSL Results", il se plaint d'une erreur dans la feuille de style ... Qu'est-ce que vous utilisez pour l'appliquer? – skerit

+0

Je pensais à faire le rembourrage de cette façon ... Je devrais simplement mon exemple étendu avant de poster, merci! –