我如何改进这个 linq to xml?

发布于 2025-01-01 07:16:33 字数 2037 浏览 1 评论 0原文

下面是 LINQ to XML 的问题:

  • 有没有办法返回属性的枚举,这样我就不必执行 foreach 循环?

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

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

  • 此外,是否可以不仅返回具有 16 位数字属性的元素,还可以返回具有内部文本的元素,例如 1234567890123456,然后解析每个子元素 的父节点的节点,例如,xml 如下所示:

    <前><代码> <详细信息>2838383838383838383990202<名字>乔

Here是代码:

    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 位数字与第一个数字相同,我只是替换该部分,所以我我猜这会带来如何更新正确属性的问题。

更新总数的解决方案是使用正则表达式边界吗?

Here are the problems with the LINQ to XML below:

  • Is there a way to return an enumeration of the attributes so I don't have to do the foreach loop?

  • It should only return elements that have a cardnum between 13 and 16 digits, but it appears to be returning numbers longer than that? Why?

  • Is long.TryParse the best way to test if the 16 digit number is in fact a number?

  • Also, is it possible to return not only elements that have attributes with 16 digit numbers, but also elements with inner text such as <ccnum>1234567890123456</ccnum> and then parse every child node of the parent node of <ccnum>, so for example, the xml would look like this:

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

Here is the code:

    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, "***");
        }
      }
    }

OK, I got why it I thought it was returning the number longer than 16, it was because, the first 16 digits are the same as the first number and I am just replacing that part, so I guess that brings up the question of how to just update the correct attribute.

Is a solution to updating the whole number is to use a regex boundary?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜点 2025-01-08 07:16:33
  • 避免 foreach 循环:

    var element = XElement.Parse(xml);
    var elementsWithPossibleCCNumbers =
        元素.后代()
                .Where(d => d.Attributes()
                    .Where(a => a.Value.Length >= 13 && a.Value.Length <= 16)
                    .Count(a => long.TryParse(a.Value, out numeric)) == 1);
    
    具有可能的 CC 编号的元素
        .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 位之间的元素 [...] - 很高兴你已经解决了这个问题 :-)

  • I思考long.TryParse 是检查所有字符是否都是数字的好方法。或者,您可以使用 @Henk Holterman 在他的答案中建议的正则表达式 - 这也摆脱了长度比较,使代码更短且更具可读性。

  • 对于具有内部文本的元素,您应该使用 element.Value 而不是 foreach(a in element.Attributes) -> a.Value

  • To avoid the foreach loop:

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

and declare this method:

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

        return "***";
    }
  • It should only return elements that have a cardnum between 13 and 16 digits [...] - glad you sorted that out :-)

  • I think long.TryParse is a good way to check if all characters are digits. Alternatively, you could use the regex that @Henk Holterman suggested in his answer - that also gets rid of the Length comparison, making the code shorter and more readable.

  • In the case of elements with inner text, you should use element.Value instead of foreach(a in element.Attributes) -> a.Value

飘落散花 2025-01-08 07:16:33

我会使用类似的东西:

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

I would use something like:

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

or, when cardnum could be missing:

     .Where(d => d.Attribute("cardnum") != null 
           && re.IsMatch(d.Attribute("cardnum").Value));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文