Как я могу улучшить этот LINQ для XML? - PullRequest
0 голосов
/ 01 февраля 2012

Ниже приведены проблемы с LINQ to XML:

  • Есть ли способ вернуть перечисление атрибутов, чтобы мне не пришлось делать цикл foreach?

  • Он должен возвращать только те элементы, которые имеют кардное число от 13 до 16 цифр, но, похоже, он возвращает числа длиннее? Почему?

  • Является ли long.TryParse лучший способ проверить, является ли 16-значный номер на самом деле число?

  • Кроме того, возможно ли вернуть не только элементы, которые имеют атрибуты с 16-значными числами, но также элементы с внутренним текстом, например <ccnum>1234567890123456</ccnum>, а затем проанализировать каждый дочерний узел родительского узла <ccnum>, так, например, XML будет выглядеть так:

     <details>
     <ccnum>283838383838383838</ccnum>
     <cvv>399</cvv>
     <exp>0202</exp>
     <name>joe</name>
     </details>
    

Вот код:

    long numeric;

    string xml = @"<Details>
    <CreditCard cardnum='1234888888823456'
    ccv='123' 
    exp='0212' 
    cardType='1' 
    name='joe' />
    <CreditCard cardnum='123488888882345633333'
    ccv='123' 
    exp='0212' 
    cardType='1' 
    name='joe' />
    </Details>";

    XElement element = XElement.Parse(xml);
    IEnumerable<XElement> elementsWithPossibleCCNumbers = 
        element.Descendants()
               .Where(d => d.Attributes()
                            .Where(a => a.Value.Length >= 13 && a.Value.Length <= 16)
                            .Where(a => long.TryParse(a.Value, out numeric))
                            .Count() == 1).Select(x=>x);


    foreach(var x in elementsWithPossibleCCNumbers)
    {
        foreach(var a in x.Attributes())
        {
        //Check if the value is a number
        if(long.TryParse(a.Value,out numeric))
        {
            //Check if value is the credit card
            if(a.Value.Length >= 13 && a.Value.Length <= 16)
                xml = xml.Replace(a.Value, string.Concat(new String('*',a.Value.Length - 4),a.Value.Substring(a.Value.Length - 4)));
            else //If value is not a credit card, replace it with ***
                xml = xml.Replace(a.Value, "***");
        }
      }
    }

ОК, я понял, почему мне показалось, что он возвращает число длиннее 16, потому что первые 16 цифр совпадают с первым числом, и я просто заменяю эту часть, так что я думаю, что это вызывает Вопрос о том, как просто обновить правильный атрибут.

Есть ли решение для обновления целого числа - использовать границу регулярного выражения?

Ответы [ 2 ]

1 голос
/ 02 февраля 2012
  • Чтобы избежать цикла foreach:

    var element = XElement.Parse(xml);
    var elementsWithPossibleCCNumbers =
        element.Descendants()
                .Where(d => d.Attributes()
                    .Where(a => a.Value.Length >= 13 && a.Value.Length <= 16)
                    .Count(a => long.TryParse(a.Value, out numeric)) == 1);
    
    elementsWithPossibleCCNumbers
        .SelectMany(e => e.Attributes())
        .Where(a => long.TryParse(a.Value, out numeric))
        .ToList()
        .ForEach(a => a.Value = a.Value.Replace(a.Value, MaskNumber(a.Value)));
    

и объявить этот метод:

    static string MaskNumber(string numericValue)
    {
        if (numericValue.Length >= 13 && numericValue.Length <= 16)
            return new String('*', numericValue.Length - 4) + numericValue.Substring(numericValue.Length - 4);

        return "***";
    }
  • Он должен возвращать только те элементы, у которых есть от 13 до 16 цифр [...] - рад, что вы разобрались с этим: -)

  • Я думаю long.TryParse это хороший способ проверить, все ли символы являются цифрами.В качестве альтернативы вы можете использовать регулярное выражение, предложенное @Henk Holterman в своем ответе, которое также избавляет от сравнения длины, делая код короче и более читабельным.

  • В случае элементовс внутренним текстом, вы должны использовать element.Value вместо foreach(a in element.Attributes) -> a.Value

1 голос
/ 02 февраля 2012

Я бы использовал что-то вроде:

    var rexCardnum = new Regex(@"^\d{13,16}$");
    var element = XElement.Parse(xml);
    var elementsWithPossibleCCNumbers =
        element.Descendants("CreditCard")
        .Where(d => rexCardnum.IsMatch(d.Attribute("cardnum").Value));

или, когда cardnum может отсутствовать:

     .Where(d => d.Attribute("cardnum") != null 
           && re.IsMatch(d.Attribute("cardnum").Value));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...