从多态 JSON 检索 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代码呢? 我想我很接近,但我得到所有的空值。 我需要更改什么才能完成这项工作?

它应该是:

   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"] 那样。 索引器将仅返回直接子级。 您需要使用SelectToken()来选择孙子孙女。

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

                 "SecuritySymbol":{  
                    "symbolset":{  
    

    有时是一个数组:

                 "SecuritySymbol":[  
                    {  
                       "symbolset":{  
    

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

下面是一个更新的查询,该查询已略作优化,以消除对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()
               };

创建一个扩展方法,该方法将采用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"],
    };

最新更新