【问题标题】:Using LINQ to query XDocument, how to get specific values?使用LINQ查询XDocument,如何获取具体值?
【发布时间】:2023-04-11 05:47:01
【问题描述】:

我正在尝试重构以下内容 - 可行,但如果我开始在 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);
}

给定以下 XML URL

<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 中只返回我需要的字段(id、可用性等),并用这个填充一个简单的类数据。

谁能帮忙?

【问题讨论】:

  • 在这种情况下,您可能不想使用XDocument。见this answer

标签: c# xml linq-to-xml xdoc


【解决方案1】:

有时您必须为自己编写的代码感到高兴。有时候没有“更聪明”的写法……只能写得“更好”一点:

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" 987654330@循环,因为从这里看不清楚,但是每个循环都重新构建。 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";
    }
    

【讨论】:

    【解决方案2】:

    尝试以下:

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

    【讨论】:

    • 是的。如果您从 httpClient 获取字符串,则需要使用 parse 方法。
    猜你喜欢
    • 1970-01-01
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    • 1970-01-01
    相关资源
    最近更新 更多