Используя LINQ для запроса XDocument, как получить конкретные значения? - PullRequest
0 голосов
/ 03 июня 2018

Я пытаюсь провести рефакторинг следующего - который работает, но если я начну получать больше элементов в 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 ]

0 голосов
/ 03 июня 2018

Иногда вы должны быть счастливы за код, который вы написали.Иногда нет более «умного» способа написать это ... Вы можете написать его немного «лучше»:

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 это довольно просто.Есть 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";
    }
    
0 голосов
/ 03 июня 2018

Попробуйте следующее:

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();
        }
    }
}
...