2016-09-16 48 views
6

下面是我擁有的json字符串。LINQ to JSON - 動態嵌套數組的設置列表

{ 
    "?xml" : { 
     "@version" : "1.0", 
     "@encoding" : "UTF-8" 
    }, 
    "DataFeed" : { 
     "@FeedName" : "issuerDetails", 
     "SecurityDetails" : { 
      "Security" : { 
       "SecurityID" : { 
        "@idValue" : "AAPL-NSDQ", 
        "@fiscalYearEnd" : "2016-12-31T00:00:00.00" 
       }, 
       "FinancialModels" : { 
        "FinancialModel" : [{ 
          "@id" : "780", 
          "@name" : "Estimates - Energy", 
          "@clientCode" : "A", 
          "Values" : [{ 
            "@name" : "EBITDA", 
            "@clientCode" : "EBITDA", 
            "@currency" : "C$", 
            "Value" : [{ 
              "@year" : "2014", 
              "#text" : "555.64" 
             }, { 
              "@year" : "2015", 
              "#text" : "-538.986" 
             }, { 
              "@year" : "2016", 
              "#text" : "554.447" 
             }, { 
              "@year" : "2017", 
              "#text" : "551.091" 
             }, { 
              "@year" : "2018", 
              "#text" : "0" 
             } 
            ] 
           }, { 
            "@name" : "EPS", 
            "@clientCode" : "EPS", 
            "@currency" : "C$", 
            "Value" : [{ 
              "@year" : "2014", 
              "#text" : "0" 
             }, { 
              "@year" : "2015", 
              "#text" : "-1.667" 
             }, { 
              "@year" : "2016", 
              "#text" : "-1.212" 
             }, { 
              "@year" : "2017", 
              "#text" : "0.202" 
             }, { 
              "@year" : "2018", 
              "#text" : "0" 
             } 
            ] 
           }, { 
            "@name" : "CFPS", 
            "@clientCode" : "CFPS", 
            "@currency" : "C$", 
            "Value" : [{ 
              "@year" : "2014", 
              "#text" : "3.196" 
             }, { 
              "@year" : "2015", 
              "#text" : "-0.207" 
             }, { 
              "@year" : "2016", 
              "#text" : "0.599" 
             }, { 
              "@year" : "2017", 
              "#text" : "2.408" 
             }, { 
              "@year" : "2018", 
              "#text" : "0" 
             } 
            ] 
           } 
          ] 
         } 
        ] 
       } 
      } 
     } 
    } 
} 

如何選擇2015年,2016年,2017年EPS的#text數據?下面是我到目前爲止查詢:

JObject jsonFeed = JObject.Parse(jsonText); 

var query = from security in jsonFeed.SelectTokens("DataFeed.SecurityDetails.Security") 
     .SelectMany(i => i.ObjectsOrSelf()) 
    let finModels = security.SelectTokens("FinancialModels.FinancialModel") 
     .SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault() 
    where finModels != null 
    select new 
    { 
     FinModelClientCode = (string)finModels.SelectToken("Values[1][email protected]"), 
     FinModelYear2015 = (string)finModels.SelectToken("Values[1].Value[1][email protected]"), 
     FinModelValue2015 = (string)finModels.SelectToken("Values[1].Value[1].#text"), 
     FinModelYear2016 = (string)finModels.SelectToken("Values[1].Value[2][email protected]"), 
     FinModelValue2016 = (string)finModels.SelectToken("Values[1].Value[2].#text"), 
     FinModelYear2017 = (string)finModels.SelectToken("Values[1].Value[3][email protected]"), 
     FinModelValue2017 = (string)finModels.SelectToken("Values[1].Value[3].#text"), 
    }; 

下面是我使用的jsonExtensions:

public static class JsonExtensions 
{ 
    public static IEnumerable<JToken> DescendantsAndSelf(this JToken node) 
    { 
     if (node == null) 
      return Enumerable.Empty<JToken>(); 
     var container = node as JContainer; 
     if (container != null) 
      return container.DescendantsAndSelf(); 
     else 
      return new[] { node }; 
    } 

    public static IEnumerable<JObject> ObjectsOrSelf(this JToken root) 
    { 
     if (root is JObject) 
      yield return (JObject)root; 
     else if (root is JContainer) 
      foreach (var item in ((JContainer)root).Children()) 
       foreach (var child in item.ObjectsOrSelf()) 
        yield return child; 
     else 
      yield break; 
    } 

    public static IEnumerable<JToken> SingleOrMultiple(this JToken source) 
    { 
     IEnumerable<JToken> arr = source as JArray; 
     return arr ?? new[] { source }; 
    } 
} 

的問題是,EPS不會永遠是下一個公司的同一個位置?所以,我希望查詢搜索EPS客戶端代碼&返回上述年份的值,希望使用DRY方法。你會好心幫我完成我的查詢嗎?

NOTE:我正在下載一個XML字符串,將它轉換爲JSON然後解析它。

XmlDocument doc = new XmlDocument(); 
doc.LoadXml(xmlString); 
jsonText = Newtonsoft.Json.JsonConvert.SerializeXmlNode(doc); 

JObject jsonFeed = JObject.Parse(jsonText); 

回答

5

我認爲最簡單的方法將反序列化的JSON到一個具體的對象像下面

var root = JsonConvert.DeserializeObject<RootObject>(jsonstring); 

你的模型應該是

public class SecurityID 
{ 
    [JsonProperty("@idValue")] 
    public string IdValue { get; set; } 
    [JsonProperty("@iscalYearEnd")] 
    public string FiscalYearEnd { get; set; } 
} 

public class Time 
{ 
    [JsonProperty("@year")] 
    public string Year { get; set; } 
    [JsonProperty("#text")] 
    public string Text { get; set; } 
} 

public class FinancialModelItem 
{ 
    [JsonProperty("@name")] 
    public string Name { get; set; } 
    [JsonProperty("@clientCode")] 
    public string ClientCode { get; set; } 
    [JsonProperty("@currency")] 
    public string Currency { get; set; } 
    public List<Time> Value { get; set; } 
} 

public class FinancialModel 
{ 
    [JsonProperty("@id")] 
    public string Id { get; set; } 
    [JsonProperty("@name")] 
    public string Name { get; set; } 
    [JsonProperty("@clientCode")] 
    public string ClientCode { get; set; } 
    public List<FinancialModelItem> Values { get; set; } 
} 

public class FinancialModels 
{ 
    public List<FinancialModel> FinancialModel { get; set; } 
} 

public class Security 
{ 
    public SecurityID SecurityID { get; set; } 
    public FinancialModels FinancialModels { get; set; } 
} 

public class SecurityDetails 
{ 
    public Security Security { get; set; } 
} 

public class DataFeed 
{ 
    [JsonProperty("@FeedName")] 
    public string FeedName { get; set; } 
    public SecurityDetails SecurityDetails { get; set; } 
} 

public class Xml 
{ 
    [JsonProperty("@version")] 
    public string Version { get; set; } 
    [JsonProperty("@encoding")] 
    public string Encoding { get; set; } 
} 

public class RootObject 
{ 
    [JsonProperty("?xml")] 
    public Xml Xml { get; set; } 
    public DataFeed DataFeed { get; set; } 
} 

和您的查詢現在是

var result = root.DataFeed.SecurityDetails.Security.FinancialModels.FinancialModel 
       .FirstOrDefault()?.Values 
       .FirstOrDefault(x => x.Name == "EPS") 
       .Value 
       .Where(x => new[] { "2015", "2016", "2017" }.Contains(x.Year)) 
       .Select(x => x.Text) 
       .ToList(); 
1

如果你確定,這將是JSON格式,您可以創建表示JSON對象類,並讓某些庫(如JconConvert)做解析。從那裏應該很容易。

+0

我寧願用我已經使用的方法。正如我所提到的,JSON將是動態的。因此,下一個對象的對象屬性可能不是靜態的。我們不能使用已經提到的某種方法嗎? –

2

什麼:

var jsonFeed = JObject.Parse(jsonText); 
var epsToken = jsonFeed.SelectToken("$..Values[?(@[email protected]=='EPS')]");    
var year2014 = epsToken.SelectToken("Value[?(@[email protected]=='2014')].#text").ToString(); 
var year2015 = epsToken.SelectToken("Value[?(@[email protected]=='2015')].#text").ToString(); 
var year2016 = epsToken.SelectToken("Value[?(@[email protected]=='2016')].#text").ToString(); 
var year2017 = epsToken.SelectToken("Value[?(@[email protected]=='2017')].#text").ToString(); 

更通用的方法,這將選擇所有年份和值:

var jsonFeed = JObject.Parse(jsonText); 
var epsToken = jsonFeed.SelectToken("$..Values[?(@[email protected]=='EPS')]");    
var years = epsToken.SelectToken("Value") 
        .Select(i => new 
        { 
         Year = i.Value<string>("@year"), 
         Value = i.Value<decimal>("#text") 
        }); 

$..意味着我們會從搜索文件的開始遍歷所有節點並搜索Values令牌,其中@name等於EPS。基本上,在?()之間,您正在輸入令牌必須滿足才能被選中的條件。 @表示當前節點,所以@[email protected]轉換爲current node which has child node with name '@name'(我們在示例中將其與EPS進行比較)。

您可以在這裏找到關於JPath的更多信息:http://goessner.net/articles/JsonPath


注意到你更新你正在處理XML的答案,所以基本保持不變:

XmlDocument doc = new XmlDocument(); 
doc.LoadXml(xmlString); 
var epsNode = doc.SelectSingleNode("//Values[@name='EPS']"); 
var years = epsNode.SelectNodes("Value") 
        .Cast<XmlNode>() 
        .Select(i => new 
        { 
         Year = i.Attributes["year"].Value, 
         Value = decimal.Parse(i.InnerText) 
        }); 

沒有測試它在你的XML。此外,請注意,i.Attributes["year"]可能是null,因此也要進行測試。

2

您從XML數據開始......爲什麼不把它作爲XML數據處理呢?

var name = "EPS"; 
var years = new[] { "2015", "2016", "2017" }; 
var xpath = $"//Values[@name='{name}']/Value[{String.Join(" or ", years.Select(y => $"@year='{y}'"))}]"; 
var values = doc.XPathSelectElements(xpath).Select(e => (decimal)e); 

否則,如果你一定要堅持使用它作爲JSON,那麼你可以這樣做:

var name = "EPS"; 
var years = new[] { "2015", "2016", "2017" }; 
var jpath = $"$..Values[?(@[email protected]=='{name}')].Value[?({String.Join(" || ", years.Select(y => $"@[email protected]=='{y}'"))})].#text"; 
var values = jsonFeed.SelectTokens(jpath).Select(v => (decimal)v);