1

我有一个很大的 XML 文件,我在其中使用小片段ReadFrom(),然后我将获得不同位置的xmlsnippet包含leaf、、、标签(有时与 kir 相比saskir叶子在顶部,反之亦然)。

现在问题是我正在使用三个foreach循环来获取这些值,这是错误的逻辑,并且当这个片段也很大时需要时间。

无论如何我可以使用一个foreach循环然后if loop在 foreach 中使用三个循环来获取值吗?

arr是一种习俗arraylist

var xdoc = new XDocument(xmlsnippet);
string xml = RemoveAllNamespaces(xdoc.ToString());
foreach (XElement element in XDocument.Parse(xml).Descendants("leaf"))
{
    arr.Add(new Test("leaf", element.Value, 2));
    break;
}
foreach (XElement element in XDocument.Parse(xml).Descendants("sas"))
{
    arr.Add(new Test("sas", element.Value, 2));
    break;
}

foreach (XElement element in XDocument.Parse(xml).Descendants("kir"))
{
    if (element.Value == "0")
        arr.Add(new Test("kir", "90", 2));
    break;
}
4

1 回答 1

2

您只需要解析该 xmlsnippet 一次(假设它适合内存),然后使用XNamespace来限定正确的 XElement。无需调用RemoveAllnamespaces它,我猜它的名字所暗示的那样,并且可能以一种可怕的方式这样做。

我使用以下 XML 片段作为示例输入,注意命名空间 a、b 和 c:

var xmlsnippet = @"<root xmlns:a=""https://a.example.com"" 
    xmlns:b=""https://b.example.com"" 
    xmlns:c=""https://c.example.com"">
    <child>
    <a:leaf>42</a:leaf>
    <a:leaf>43</a:leaf>
    <a:leaf>44</a:leaf>
    <somenode>
    <b:sas>4242</b:sas>
    <b:sas>4343</b:sas>
    </somenode>
    <other>
    <c:kir>80292</c:kir>
    <c:kir>0</c:kir>
    </other>
    </child>
</root>";

然后使用 Linq 如果您的 Test 类返回一个实例,或者如果找不到元素则返回 null。然后将该 Test 类实例添加到数组列表中。

var arr = new ArrayList();

var xdoc = XDocument.Parse(xmlsnippet);

// add namespaces
var nsa = (XNamespace) "https://a.example.com";
var nsb = (XNamespace) "https://b.example.com";
var nsc = (XNamespace) "https://c.example.com";

var leaf = xdoc.Descendants(nsa + "leaf").
    Select(elem => new Test("leaf", elem.Value, 2)).FirstOrDefault();
if (leaf != null) {
    arr.Add(leaf);
}
var sas = xdoc.Descendants(nsb + "sas").
    Select(elem => new Test("sas", elem.Value, 2)).FirstOrDefault();
if (sas != null) {
    arr.Add(sas);
}
var kir = xdoc.
    Descendants(nsc + "kir").
    Where(ele => ele.Value == "0").
    Select(elem => new Test("kir", "90", 2)).
    FirstOrDefault();
if (kir != null) {
    arr.Add(kir);
}

如果您想坚持使用 XDocument,我希望这是查找这些节点的最有效方法。如果 xml 真的很大,您可能会考虑使用 XMLReader,但这可能仅在内存有问题时才有帮助。

如果你想做一个 LINQ 查询,你可以这样做:

 var q =  xdoc
    .Descendants()
    .Where(elem => elem.Name.LocalName == "leaf" ||
                   elem.Name.LocalName == "sas" ||
                   elem.Name.LocalName == "kir" && elem.Value == "0" )
    .GroupBy(k=> k.Name.LocalName)
    .Select(k=>
        new Test(
            k.Key, 
            k.Key != "kir"? k.FirstOrDefault().Value: "90",
            2)
    );
 arr.AddRange(q.ToList());

该查询查找所有名为leaf、sas 或kir 的元素,将它们按元素名分组,然后获取每个组中的第一个元素。注意如果元素名是 kir 的额外处理。where 子句和投影都Select需要处理这个问题。您可能想对此进行性能测试,因为我不确定这会有多有效。

为了完整起见,这里是一个 XmlReader 版本:

var state = FoundElement.NONE; 
using(var xe = XmlReader.Create(new StringReader(xmlsnippet)))
while (xe.Read())
{ 
    // if we have not yet found an specific element
    if (((state & FoundElement.Leaf) != FoundElement.Leaf) && 
       xe.LocalName == "leaf") 
    {
       // add it ... do not change the order of those arguments
       arr.Add(new Test(xe.LocalName, xe.ReadElementContentAsString(), 2));
       // keep track what we already handled.
       state = state | FoundElement.Leaf;
    }
    if (((state & FoundElement.Sas) != FoundElement.Sas) && 
        xe.LocalName == "sas") 
    {
        arr.Add(new Test(xe.LocalName, xe.ReadElementContentAsString(), 2));
        state = state | FoundElement.Sas;
    }
    if (((state & FoundElement.Kir) != FoundElement.Kir) && 
        xe.LocalName == "kir") 
    {
        var localName = xe.LocalName; // we need this ...
        var cnt = xe.ReadElementContentAsString();  // ... because this moves the reader
        if (cnt == "0") {
            arr.Add(new Test(localName, "90", 2));
            state = state | FoundElement.Kir;
        }
    }
}

这是具有不同状态的枚举。

[Flags]
enum FoundElement
{
   NONE =0,
   Leaf = 1,
   Sas = 2,
   Kir = 4
}
于 2018-04-14T08:51:08.777 回答