如何将这个linq改进为xml?

时间:2012-02-01 19:42:00

标签: c# linq-to-xml

以下是LINQ to XML的问题:

  • 有没有办法返回属性的枚举,所以我不必进行foreach循环?

  • 它应该只返回卡号在13到16位之间的元素,但它似乎返回的数字比这长?为什么呢?

  • 是long.TryParse是测试16位数字实际上是数字的最佳方法吗?

  • 此外,是否有可能不仅返回具有16位数字属性的元素,还返回具有内部文本的元素(如<ccnum>1234567890123456</ccnum>),然后解析父节点<ccnum>的每个子节点。 1}},例如,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 个答案:

答案 0 :(得分:1)

我会使用类似的东西:

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

答案 1 :(得分:1)

  • 为了避免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) - &gt; a.Value