Fusione libera di documenti XML
-
06-07-2019 - |
Domanda
Ho due documenti: uno è un formato di file XML personalizzato, l'altro è un feed RSS con una serie di estensioni personalizzate. Voglio compilare i campi nel file XML con i valori trovati nel feed RSS quando un valore di elemento corrisponde.
Questo è per un processo offline che verrà eseguito alcune volte manualmente - non deve funzionare bene, essere tollerante ai guasti, ecc. Il lavoro manuale o l'intervento vanno bene.
Il mio documento XML principale è simile al seguente:
<videos>
<video>
<title>First Video</title>
<code>AAA123</code>
<id>decaf-decaf-decaf-decaf</id>
<description>lots of text here...</description>
</video>
<video>
<title>Second Video with no code</title>
<code></code>
<id>badab-badab-badab-badab</id>
<description>lots of text here...</description>
</video>
</videos>
Il feed RSS è un RSS standard con qualche campo aggiuntivo:
<ns:code>AAA123</ns:code>
<ns:type>Awesome</ns:type>
<ns:group>Wonderful</ns:group>
Vorrei estrarre i campi extra dal documento RSS nel documento XML quando il valore corrisponde al valore:
<videos>
<video>
<title>First Video</title>
<code>AAA123</code>
<id>decaf-decaf-decaf-decaf</id>
<description>lots of text here...</description>
<type>Awesome</type>
<group>Wonderful</group>
</video>
<video>
<title>Second Video with no code</title>
<code></code>
<id>badab-badab-badab-badab</id>
<description>lots of text here...</description>
<type></type>
<group></group>
</video>
</videos>
Mi piacerebbe di più usare c #, LINQ o qualche tipo di Excel-fu. Immagino che se dovessi, potrei avere a che fare con XSLT purché non mi coinvolga a scrivere molto XSLT da solo.
Ho esaminato questa domanda, ma non mi è sembrato molto utile per quello che sto cercando di fare: Unisci documenti XML
Soluzione
Sembra un lavoro per LINQ to XML!
var vidDoc = XDocument.Parse(vidXml);
var rssDoc = XDocument.Parse(rssXml);
var videos = vidDoc.XPathSelectElements("/videos/video");
var rssItems = rssDoc.XPathSelectElements("/rss/channel/item");
var matches = videos.Join(
rssItems,
video => video.Element(XName.Get("code")).Value,
rssItem => rssItem.Element(XName.Get("code", "http://test.com")).Value,
(video, item) => new {video, item});
foreach (var match in matches)
{
var children = match.item.Elements()
.Where(child => child.Name.NamespaceName == "http://test.com" &&
child.Name.LocalName != "code");
foreach (var child in children)
{
//remove the namespace
child.Name = XName.Get(child.Name.LocalName);
match.video.Add(child);
}
}
vidDoc.Save(Console.Out);
La soluzione sopra presuppone che il documento RSS sia simile al seguente:
<rss xmlns:ns="http://test.com" version="2.0">
<channel>
<item>
<title>AAA123</title>
<link>http://test.com/AAA123</link>
<pubDate>Sun, 26 Jul 2009 23:59:59 -0800</pubDate>
<ns:code>AAA123</ns:code>
<ns:type>Awesome</ns:type>
<ns:group>Wonderful</ns:group>
</item>
</channel>
</rss>
Altri suggerimenti
Aggiungi questo a una trasformazione dell'identità XSLT (dovrai anche aggiungere la dichiarazione dello spazio dei nomi per lo spazio dei nomi http://test.com
all'elemento di livello superiore della trasformazione):
<xsl:variable name="rss" select="document('rss.xml')"/>
<xsl:template match="video">
<xsl:apply-templates select="@* | node()"/>
<xsl:apply-templates select="$rss/rss/channel/item[ns:code=current()/code]"/>
</xsl:template>
<!-- this keeps the code element from getting copied -->
<xsl:template match="ns:code"/>
<!-- this will copy all of the content of the ns:* elements, not just their text -->
<xsl:template match="ns:*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
Se hai già letto l'RSS in un XmlDocument
nel tuo programma, puoi passarlo all'XSLT come parametro invece di usare la funzione document ()
per leggerlo.