2016-03-16 49 views
2

我有一個問題,使用NewtonSoft v8.0.3 JToken.SelectTokens()與特定的JsonPath。在C#中重現問題:問題/ Bug使用SelectTokens與JsonPath

 string json = "[{\"name\":\"string\",\"value\":\"aString\"},{\"name\":\"number\",\"value\":123},{\"name\":\"array\",\"value\":[1,2,3,4]},{\"name\":\"object\",\"value\":{\"1\":1}}]"; 
     string path = "$.[?(@.value!=null)]"; 
     string newJson = String.Empty; 

     JToken jt = JToken.Parse(json); 
     IEnumerable<JToken> tokens = jt.SelectTokens(path, false); 
     newJson = JsonConvert.SerializeObject(tokens, Formatting.Indented); 
     // Output is missing objects and arrays?? 
     // newJson = [{"name": "string","value": "aString"},{"name": "number","value": 123}] 

運行上面的代碼將不會返回任何對象或數組? Json.Net的JToken.SelectTokens()中是否存在錯誤?

爲了驗證我已經使用http://goessner.net/articles/JsonPath/工作正常的路徑,看到這個工作fiddle

我希望有人能照到這個一些。

UPDATE: 思考這個問題發生,因爲我們正在嘗試使用「空」的值,但是不管我跑什麼不平等查詢,沒有{}/[]從JSON返回。

回答

0

終於想出了在使用JSONPath時,導致.SelectTokens忽略數組和對象的原因。發生在QueryExpressions.cs line 78

JValue v = r as JValue; 

這個問題會造成任何JToken是JObject/JArray成爲,因此比較時被忽略。我的這種固定方式是,以取代用下面的代碼來處理JObject和JArray線78:

  JValue v = null; 
      switch (r.Type) 
      { 
       case JTokenType.Object: 
       case JTokenType.Array: 
        v = new JValue(r.ToString()); 
        break; 
       default: 
        v = r as JValue; 
        break; 
      } 

這將確保調用.SelectTokens時JObject和JArray返回。請隨時評論解決方案或建議替代和更好的方法。

更新:很高興通知此問題已在新版本中解決。有關更多信息,請參閱GITHUB

+0

我很高興地通知這個問題已經在新版本中修復。有關更多信息,請參閱https://github.com/JamesNK/Newtonsoft.Json/commit/031222d4f4bd541e66f236329591f2427f835174。 – mrw

2

我可以SelectTokens()使用存在運營商,而不是運營商的不平等返回你想要的結果:

string path = "$.[?(@.value)]"; 

現在將返回所有4個對象。樣品fiddle

至於這是否是一個bug,JSONPath article是不明確的。它說:

[] ?()  applies a filter (script) expression. 
n/a ()  script expression, using the underlying script engine. 

但是,使用底層腳本引擎並實際上意味着什麼呢?如果我在https://jsonpath.curiousconcept.com/測試您的JSON和查詢表達式,則會爲Flow Communications JSONPath 0.3.1Stefan Goessner JSONPath 0.8.3生成錯誤。如果我使用我的存在查詢,那麼前者工作正常,但後者仍會引發異常。

Json.NET應該像現在一樣運行嗎?本實現隱含地提供了對具有原始值的屬性進行過濾的能力。建議的行爲會使失去這種能力。即,任何對象或數組值都將始終匹配任何不等式運算符,如"$.[?(@.value!='aString')]",這可能不是預期的。

更新

你澄清的要求是運行一個查詢"$.[?(@.value!=null)]"與一個名爲"value"存在並具有非空值,無論該值是否爲原始或嵌套容器屬性返回所有對象。

一方面,這似乎是一個合理的查詢應該是可能的。另一方面,目前能夠運行對原始值的不等式查詢而沒有獲得所有容器值的能力似乎是有用的,應該也是可能的。並且對特定查詢字符串的解釋似乎是合理的; JSONPath語言的定義太模糊,並且太落後於實現者的解釋。

你選擇,讓你需要的東西包括:

  1. ,你可以肯定report an issue這一點。Newtonsoft可以決定當前的行爲是錯誤的,並改變它。

  2. 你可以派生自己Json.NET的版本和修改BooleanQueryExpression.IsMatch(JToken t)如下:使用擴展方法

    var path = "$.[?(@.value)]"; 
    var tokens = jt.SelectTokens(path, false) 
        .Where(t => !t["value"].IsNull()); 
    

      case QueryOperator.NotEquals: 
           if (v != null && !EqualsWithStringCoercion(v, Value)) 
           { 
            return true; 
           } 
           else if (v == null && r != null) 
           { 
            return true; 
           } 
           break; 
    
  3. 您可以使用以下解決方法

    public static class JsonExtensions 
    { 
        public static bool IsNull(this JToken token) 
        { 
         return token == null || token.Type == JTokenType.Null; 
        } 
    } 
    

最後,你問我不明白的是爲什麼它在JavaScript中工作正常,但沒有在Json.Net的selectTokens中。。該Json.NET是不同的行爲比說,http://jsonpath.com/究其原因,就在於:

  1. 的標準是什麼一個script expression是,正是完全模糊,

  2. 他們使用不同的實現技術和代碼庫。 Json.NET解釋(@.value!=null)意指

    名爲「值」 JValue類型的值,該值不等於所述null JSON原語。

    而其它解析器解釋查詢作爲

    名爲「值」,也就是不等於null JSON原始值。

    與javascript相比,c#強類型的事實可能解釋了這種差異。

+0

謝謝回答!對不起,但應該提到「$。[?(@。value!= null)]」的全部用途是隻返回鍵值爲「value」的對象。挑戰是避免返回即。 ** { 「名」: 「testnull」, 「價值」:空}。關於在線JSONPath測試,有一些差異,我不明白爲什麼。在您提到的網站上進行測試,但是我確實發現了錯誤;可以在www.jsonpath.com或www.jsonquerytool.com上使用「$。[?(@。value!= null)]」。我不明白的是爲什麼它在javascript中正常工作,但不在Json.Net的selectTokens中。任何想法解決? – mrw

+0

@mrw - 答案已更新。 – dbc

+0

很好的回答!感謝澄清。將報告爲問題,並開始調查選項2,因爲我不想要使用LINQ的「值」鍵的靜態檢查。在其他JSON中可能是另一個關鍵字。 – mrw

0

Jsonpath沒有標準的實現,所以你的結果會根據你使用的是哪個分析器而有所不同。然而,你永遠不需要檢查空值,因爲它對Jsonpath沒有任何意義。

這是你所需要的:

var result = jt.SelectTokens("$.[?(@.value)]"); 
+0

我想你已經理解了這個問題。我不檢查空值,我避免返回潛在令牌,其中對象鍵「值」可能是空的使用JsonPath。問題已經確定並報告給Newtonsoft。 – mrw

+0

是的,我的不好。我在瀏覽器中試過,並且沒有打算驗證Json.NET是否符合該行爲。然而,我的觀點仍然堅持'!= null'仍然是不必要的 - 如果/當bug修復時,我發佈的Jsonpath應該會給你你想要的結果。 – returnsvoid