Используя LINQ для запроса XDocument, как получить конкретные значения?
Я пытаюсь реорганизовать следующее - это работает, но если я начну получать больше элементов в XML, это станет неуправляемым:
HttpResponseMessage response = await httpClient.GetAsync("https://uri/products.xml");
string responseAsString = await response.Content.ReadAsStringAsync();
List<Product> productList = new List<Product>();
XDocument xdocument = XDocument.Parse(responseAsString);
var products = xdocument.Descendants().Where(p => p.Name.LocalName == "item");
foreach(var product in products)
{
var thisProduct = new Product();
foreach (XElement el in product.Nodes())
{
if(el.Name.LocalName == "id")
{
thisProduct.SKU = el.Value.Replace("-master", "");
}
if (el.Name.LocalName == "availability")
{
thisProduct.Availability = el.Value == "in stock";
}
}
productList.Add(thisProduct);
}
Учитывая следующий URL-адрес XML
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://base.google.com/ns/1.0" version="0">
<channel>
<title>Product Feed</title>
<link></link>
<description>Products</description>
<item>
<availability>in stock</availability>
<id>01234-master</id>
...
</item>
<item>
<availability>in stock</availability>
<id>abcde-master</id>
...
</item>
</channel>
</rss>
В идеале я хотел бы удалить циклы и операторы if и иметь запрос LINQ, который возвращает из поля XML только нужные мне поля (идентификатор, доступность и т. Д.) В хорошем чистом виде и заполнить простой класс этими данными.
Кто-нибудь может помочь?
2 ответа
Иногда вы должны быть счастливы за код, который вы написали. Иногда нет более "умного" способа написать это... Вы можете написать это немного "лучше":
List<Product> productList = new List<Product>();
XDocument xdocument = XDocument.Parse(responseAsString);
XNamespace ns = "http://base.google.com/ns/1.0";
var products = from x in xdocument.Elements(ns + "rss")
from y in x.Elements(ns + "channel")
from z in y.Elements(ns + "item")
select z;
foreach (var product in products)
{
var prod = new Product();
productList.Add(prod);
foreach (XElement el in product.Elements())
{
if (el.Name == ns + "id")
{
prod.SKU = el.Value.Replace("-master", string.Empty);
}
else if (el.Name == ns + "availability")
{
prod.Availability = el.Value == "in stock";
}
}
}
Заметки:
Descendants()
морально неправильно. Существует фиксированная позиция, гдеitem
будет,/rss/channel/item
и вы это прекрасно знаете. Это не//item
, Потому что завтра может бытьrss/foo/item
что сегодня не существует. Вы пытаетесь написать свой код так, чтобы он был совместим с дополнительной информацией, которая может быть добавлена в xml.Я ненавижу пространства имен XML... И есть XML с несколькими вложенными пространствами имен. Как сильно я ненавижу их. Но кто-то умнее меня решил, что они существуют. Я принимаю это. Я кодирую их используя. В LINQ-to-XML это довольно просто. E сть
XNamespace
что даже перегружено+
оператор.Обратите внимание, что если вы микрооптимизатор (я стараюсь этого не делать, но должен признать, что у меня немного чешется руки), вы можете предварительно рассчитать различные
ns + "xxx"
которые используются внутриfor
цикл, потому что это не ясно, отсюда, но все они перестраиваются каждый цикл. КакXName
построен внутри... о... это увлекательная вещь, поверь мне.private static readonly XNamespace googleNs = "http://base.google.com/ns/1.0"; private static readonly XName idName = googleNs + "id"; private static readonly XName availabilityName = googleNs + "availability";
а потом
if (el.Name == idName) { prod.SKU = el.Value.Replace("-master", string.Empty); } else if (el.Name == availabilityName) { prod.Availability = el.Value == "in stock"; }
Попробуйте следующее:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
new Item(FILENAME);
}
}
public class Item
{
public static List<Item> items { get; set; }
public string availability { get; set; }
public string id { get; set; }
public Item() { }
public Item(string filename)
{
string xml = File.ReadAllText(filename);
XDocument doc = XDocument.Parse(xml);
XElement root = doc.Root;
XNamespace ns = root.GetDefaultNamespace();
Item.items = doc.Descendants(ns + "item").Select(x => new Item() {
availability = (string)x.Element(ns + "availability"),
id = (string)x.Element(ns + "id")
}).ToList();
}
}
}