LINQ to Json数据从多态json中检索

时间:2016-06-13 15:35:35

标签: c# linq json.net linq-to-json

我有一个多态json字符串。这是它的样子:

{  
   "?xml":{  
      "@version":"1.0",
      "@encoding":"UTF-8"
   },
   "DataFeed":{  
      "@FeedName":"AdminData",
      "Issuer":[  
         {  
            "name":"Apple",
            "symbol":"AAPL-O",
            "active":"1",
            "securities":{  
               "Security":{  
                  "sedol":"B0XXF09",
                  "coverage":{  
                     "Coverage":{  
                        "analyst":{  
                           "@firstName":"Steve",
                           "@lastName":"Jobs",
                           "@rank":"1"
                        }
                     }
                  },
                  "symbolMappingList":{  
                     "SecuritySymbol":{  
                        "symbolset":{  
                           "id":"11",
                           "symbol":"ISIN",
                           "name":"ISIN",
                           "rixmlName":"ISIN",
                           "researchDirect":"S&P"
                        },
                        "symbol":"US44919P5XXX"
                     }
                  },
                  "symbolMapping":{  
                     "entry":{  
                        "int":"11",
                        "SecuritySymbol":{  
                           "@reference":"../../../symbolMappingList/SecuritySymbol"
                        }
                     }
                  },
                  "customFields":{  
                     "customField":[  
                        {  
                           "@name":"ADP",
                           "@type":"Textbox",
                           "values":{  
                              "value":"H0192XX"
                           }
                        },
                        {  
                           "@name":"Top 15",
                           "@type":"Dropdown, multiple choice",
                           "values":null
                        }
                     ]
                  }
               }
            }
         },
         {  
            "name":"Microsoft",
            "symbol":"MSFT-OTC",
            "active":"1",
            "securities":{  
               "Security":{  
                  "sedol":"B8FW54",
                  "coverage":{  
                     "Coverage":{  
                        "analyst":{  
                           "@firstName":"Bill",
                           "@lastName":"Gates",
                           "@rank":"1"
                        }
                     }
                  },
                  "symbolMappingList":{  
                     "SecuritySymbol":[  
                        {  
                           "symbolset":{  
                              "id":"3",
                              "symbol":"CUSIP",
                              "name":"CUSIP",
                              "rixmlName":"CUSIP",
                              "researchDirect":"S&P"
                           },
                           "symbol":"04316A1XX"
                        },
                        {  
                           "symbolset":{  
                              "id":"11",
                              "symbol":"ISIN",
                              "name":"ISIN",
                              "rixmlName":"ISIN",
                              "researchDirect":"S&P"
                           },
                           "symbol":"US04316A10XX"
                        }
                     ]
                  },
                  "symbolMapping":{  
                     "entry":[  
                        {  
                           "int":"3",
                           "SecuritySymbol":{  
                              "@reference":"../../../symbolMappingList/SecuritySymbol"
                           }
                        },
                        {  
                           "int":"11",
                           "SecuritySymbol":{  
                              "@reference":"../../../symbolMappingList/SecuritySymbol[2]"
                           }
                        }
                     ]
                  },
                  "customFields":{  
                     "customField":[  
                        {  
                           "@name":"ADP Security Code",
                           "@type":"Textbox",
                           "values":null
                        },
                        {  
                           "@name":"Top 15",
                           "@type":"Dropdown, multiple choice",
                           "values":null
                        }
                     ]
                  }
               }
            }
         }
      ]
   }
}

曾经有人帮助我使用扩展类,以便我可以检索ADP代码。这是扩展类:

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

基于此,这是我的疑问:

var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
           where (string)issuer["active"] == "1"
           let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
           let securitySymbol = issuer.SelectTokens("securities.Security.symbolMappingList")
            .SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
           where security != null
           select new
{
               CompName = (string)issuer["name"],
               SEDOL = ((string)security["sedol"]).StartsWith("0") ? 
                String.Format("'{0}", (string)security["sedol"]) : (string)security["sedol"],
               ADP = security["customFields"]
                .DescendantsAndSelf()
                .OfType<JObject>()
                .Where(o => (string)o["@name"] == "ADP Security Code")
                .Select(o => (string)o.SelectToken("values.value"))
                .FirstOrDefault(),
               ISIN = security["symbolMappingList"]
                .DescendantsAndSelf()
                .OfType<JObject>()
                .Where(o => (string)o["SecuritySymbol.symbolset.name"] == "ISIN")
                .Select(o => (string)o.SelectToken("SecuritySymbol.symbol"))
                .FirstOrDefault()
};

我能够获得ADP代码。但是我如何获得ISIN代码?我觉得我非常接近,但我得到的都是空的。我需要做些什么才能使其发挥作用?

2 个答案:

答案 0 :(得分:1)

应该是:

   ISIN = security["symbolMappingList"]
    .DescendantsAndSelf()
    .OfType<JObject>()
    .Where(o => (string)o.SelectToken("symbolset.name") == "ISIN")
    .Select(o => (string)o.SelectToken("symbol"))
    .FirstOrDefault()

一些注意事项:

  1. 当您尝试使用表达式o["SecuritySymbol.symbolset.name"]时,无法使用indexers来选择深层嵌套的标记。索引者只会返回直系孩子。您需要使用SelectToken()来选择孙子。

  2. "SecuritySymbol"属性的值有时是一个对象:

                 "SecuritySymbol":{  
                    "symbolset":{  
    

    有时是阵列:

                 "SecuritySymbol":[  
                    {  
                       "symbolset":{  
    

    取决于其中的物品数量。由于这种多态性,你不能只做SelectToken("SecuritySymbol.symbolset.name")。而是使用DescendantsAndSelf()递归搜索具有相应SecuritySymbol子令牌的对象的"symbolset.name"及其后代(无论是否嵌入在数组中)的值。

  3. 这是一个经过略微优化的更新查询,可以消除对SelectToken的重复调用,也可以过滤"sedol"名称:

    var filterString = "B0XXF09"; // Null if filtering is not desired
    
    var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
                   where (string)issuer["active"] == "1"
                   let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
                   where security != null
                   let sedol = (string)security["sedol"]
                   where (sedol != null && filterString == null || sedol.Contains(filterString))
                   select new
                   {
                       CompName = (string)issuer["name"],
                       SEDOL = sedol.StartsWith("0") ? String.Format("'{0}", sedol) : sedol,
                       ADP = security["customFields"]
                        .DescendantsAndSelf()
                        .OfType<JObject>()
                        .Where(o => (string)o["@name"] == "ADP Security Code")
                        .Select(o => (string)o.SelectToken("values.value"))
                        .FirstOrDefault(),
                       ISIN = security["symbolMappingList"]
                        .DescendantsAndSelf()
                        .OfType<JObject>()
                        .Where(o => (string)o.SelectToken("symbolset.name") == "ISIN")
                        .Select(o => (string)o.SelectToken("symbol"))
                        .FirstOrDefault()
                   };
    

答案 1 :(得分:0)

创建一个扩展方法,该方法将占用JToken并返回IEnumerable<JToken>。如果是一个数组,它将返回该数组,否则它将返回一个(假定的)单个标记的数组。这将为您提供一致的界面,无论是数组还是单个对象。

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

然后相应地构建您的查询。

var query =
    from issuer in obj.SelectTokens("DataFeed.Issuer[*]")
    where (int)issuer["active"] == 1
    let security = issuer.SelectToken("securities.Security") as JObject
    where security != null
    let sedol = (string)security["sedol"]
    let customFields = JObject.FromObject(
        security.SelectTokens("customFields.customField[*]")
            .ToDictionary(
                o => (string)o["@name"],
                o => (string)o.SelectToken("values.value")
            )
    )
    let symbolMapping = JObject.FromObject(
        security.SelectToken("symbolMappingList.SecuritySymbol").SingleOrMultiple()
            .ToDictionary(
                o => (string)o.SelectToken("symbolset.name"),
                o => (string)o.SelectToken("symbolset.symbol")
            )
    )
    select new
    {
        CompanyName = (string)issuer["name"],
        Sedol = sedol.StartsWith("0") ? $"'{sedol}" : sedol,
        Adp = customFields["ADP"],
        Isin = symbolMapping["ISIN"],
    };