2012-02-01 52 views
0

下面是與LINQ的問題,以下XML:如何將此linq改進爲xml?

  • 有沒有辦法返回屬性的枚舉,所以我沒有做foreach循環?

  • 它應該只返回具有13到16位數之間的cardnum的元素,但它看起來是返回的數字比那更長?爲什麼?

  • 是long.TryParse測試16位數字實際上是否是數字的最佳方法?

  • 此外,是否有可能返回不僅有16位數字屬性的元素,而且還與內文如<ccnum>123456789</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位數字與第一個數字相同,我只是替換該部分,所以我想這引起了一個問題如何更新正確的屬性。

更新整數的解決方案是使用正則表達式邊界?

+0

UMH你爲什麼不直接返回了'ccnum'元素的值?這裏你不是真的「使用」XML。 – BrokenGlass 2012-02-01 20:07:16

+0

@BrokenGlass - 因爲它在所有情況下都不叫ccnum,所以我按照它的長度來確定持有信用卡數據的元素/屬性。 – Xaisoft 2012-02-01 20:08:53

+1

雖然這是可能的,但它真的在我看來打敗了目的 – BrokenGlass 2012-02-01 20:10:29

回答

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位之間的cardnum中的元素[...] - 很高興你整理了這一點:-)

  • 我認爲long.TryParse是檢查所有字符是否都是數字的好方法。或者,你可以使用@Henk Holterman在他的答案中提出的正則表達式 - 也可以擺脫長度比較,使代碼更短,更易讀。

  • 與內部文本元素的情況下,你應該使用element.Value代替foreach(a in element.Attributes) - >a.Value

+0

我比較這與我的原始版本,他們跑來跑去。 – Xaisoft 2012-02-02 20:29:57

+0

我認爲這回答了問題。 – GolfWolf 2012-02-02 20:35:19

+0

我會給你信用。謝謝。 – Xaisoft 2012-02-02 20:42:50

1

我會使用類似:

​​

,或者當cardnum可能會錯過:

 .Where(d => d.Attribute("cardnum") != null 
      && re.IsMatch(d.Attribute("cardnum").Value)); 
+0

我有一些東西接近這個。 – Xaisoft 2012-02-01 20:31:48