Улучшение моего способа выбора нескольких элементов XE с уникальными значениями для построения списка - PullRequest
4 голосов
/ 13 января 2020

Есть ли способ улучшить это? Я чувствую, что у меня есть путь ко многим петлям.

Код создает строку с именами доменов из документа XML. В зависимости от того, находится ли имя домена в XML do c в элементах hostSW (хост начинается с), hostCN (хост содержит) или hostEW (хост EndsWith), зависит от того, нужно ли мне добавлять * в конец, начало + конец или начало значения соответственно.

Я использовал Hashset как способ убедиться, что дублирования нет.

var startWith = xdoc.Root
    .Descendants("Test")
    .Elements("hostSW")
    .ToList();
var contains = xdoc.Root
    .Descendants("Test")
    .Elements("hostCN")
    .ToList();
var endsWith = xdoc.Root
    .Descendants("Test")
    .Elements("hostEW")
    .ToList();

HashSet<string> domains = new HashSet<string>(); //use hashset so we don't duplicate results

foreach (XElement test in startWith)
{
    domains.Add(test.Value.ToString() + "*");
}
foreach (XElement test in contains)
{
    domains.Add("*" + test.Value.ToString() + "*");
}
foreach (XElement test in endsWith)
{
    domains.Add("*" + test.Value.ToString());
}

string out = "BEGIN FILE ";
foreach (string domain in domains.ToArray())
{
    out += "BEGIN DOMAIN ";
    out += domain;
    out += " END DOMAIN";
}
out += " END FILE;

return out;

XML file

<Tests>
    <Test>
        <hostSW>startsWith1</hostSW>
    </Test>
    <Test>
        <hostSW>startsWith2</hostSW>
    </Test>
    <Test>
        <hostCN>contains1</hostCN>
    </Test>
    <Test>
        <hostEW>endsWith1</hostEW>
    </Test>
</Tests>

Ответы [ 4 ]

2 голосов
/ 15 января 2020

Используя Linq для XML, вы можете сделать:

var t = xml.Root.Descendants("Test")
    .Elements()
    .Where(x => x.Name == "hostSW" || x.Name == "hostCN" || x.Name == "hostEW")
    .Select(x => (x.Name == "hostSW") ? 
                       $"{x.Value}*" 
                       : 
                       (x.Name == "hostCN" ? 
                           $"*{x.Value}*" 
                           :
                           $"*{x.Value}"));

Если ваши <Test> элементы не содержат ничего, кроме <hostSW>, <hostCN> или <hostEW>, то вы можете опустить Where.

Демонстрация здесь

2 голосов
/ 15 января 2020

Вы пытались работать с SelectSingleNode или SelectNodes с классом XmlNode ? Я знаю, что вы пытаетесь избежать циклов, но для этого сценария вы должны получить временную сложность O (n), если мы знаем, что root (родительские узлы) остаются неизменными.

Это похоже, в вашем XML файле есть узлы, содержащие нужный вам внутренний текст, поэтому вы можете сделать что-то вроде этого:

foreach (XmlNode node in xmlDoc.SelectNodes($"//Test/Test"))
{
    switch (node.Name)
    {
        case "hostSW":
            var newInnerText = node.InnerText + '*';
            //do something
            break;
        case "hostCN":
            var newInnerText = node.InnerText + '*';
            //do something
            break;
        case "hostEW":
            //do something
            break;
        default:
            //do something
            break;
    }
}
2 голосов
/ 15 января 2020

Предполагая структуру XML, подобную этой

<Root>
    <Test>
        <hostSW>startsWith1</hostSW>
    </Test>
    <Test>
        <hostCN>contains1</hostCN>
    </Test>
    <Test>
        <hostCN>contains2</hostCN>
    </Test>
    <Test>
        <hostEW>endsWith1</hostEW>
    </Test>
    <Test>
        <hostEW>endsWith2</hostEW>
    </Test>
    <Test>
        <hostEW>endsWith3</hostEW>
    </Test>
</Root>

Я получил все домены правильно и только 6 раз повторил с этим кодом

// Loop each of the elements one time without filtering element name
foreach (var test in xDoc.Root.Descendants("Test").Elements())
{
    // Switch on the name of the element.
    switch (test.Name.LocalName)
    {
        case "hostSW": { domains.Add(test.Value + "*"); } break;
        case "hostCN": { domains.Add("*" + test.Value + "*"); } break;
        case "hostEW": { domains.Add("*" + test.Value); } break;
    }
    // For any other elements we iterate just do nothing
}
0 голосов
/ 21 января 2020

Вот еще один способ. При этом используется LINQ Select вместо l oop, а вместо switch / троичное дерево используется Dictionary. Он также использует механизм замещения: в этом случае я использовал string.Format, но многие другие, включая string.Replace, были бы аналогичным образом подходящими. Все зависит от того, что вы собираетесь. Я обычно go за разумную производительность с экономичным синтаксисом, который обычно приносит хорошие баллы за простоту обслуживания.

static Dictionary<string, string> dict = new Dictionary<string, string>() {
    { "hostSW", "{0}*" },
    { "hostCN", "*{0}*" },
    { "hostEW", "*{0}" },
};

static IEnumerable<string> WildcardStringsFromXML(XDocument xdoc)
{
    return xdoc.Root.Descendants("Test").Elements()
               .Select(item => string.Format(dict[item.Name.LocalName], item.Value));
}
...