如何将这个linq改进为xml
本文关键字:xml linq | 更新日期: 2023-09-27 18:22:05
以下是LINQ to XML的问题:
-
有没有一种方法可以返回属性的枚举,这样我就不必执行foreach循环了?
-
它应该只返回卡号在13到16位之间的元素,但它似乎返回的数字比这长?为什么?
-
很长。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位数字与第一个数字相同,我只是替换了那个部分,所以我想这就引出了如何更新正确属性的问题。
更新整数的解决方案是使用正则表达式边界吗?
-
避免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
我会使用这样的东西:
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));