【问题标题】:LINQ to Json data retrieval from polymorphic json从多态 json 检索 LINQ to Json 数据
【发布时间】:2016-06-13 15:35:35
【问题描述】:

我有一个多态的 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 代码?我想我已经很接近了,但是我得到了所有的空值。我需要进行哪些更改才能完成这项工作?

【问题讨论】:

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


    【解决方案1】:

    应该是:

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

    一些注意事项:

    1. 您不能使用 indexers 来选择深度嵌套的标记,就像您尝试使用表达式 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()
                   };
    

    【讨论】:

    • 成功了。谢谢。后续问题,是否可以在 Where 子句中执行 SelectToken。即where (string)issuer["active"] == "1" &amp;&amp; security.SelectToken("sedol").Contains("B0XXF09") ?
    • @inquisitive_one - 我不明白为什么不这样做。你试过了吗?出了什么问题?注意security必须在使用前定义,所以不能在where (string)issuer["active"] == "1"子句中做检查。
    • @inquisitive_one - 如果您稍后要在select 中使用相同的值,您可能不想在where 子句中使用SelectToken。它会起作用,但使用单独的let 会更清楚。查看更新的答案。
    【解决方案2】:

    创建一个采用JToken 并返回IEnumerable&lt;JToken&gt; 的扩展方法。如果是数组,它将返回数组,否则它将返回(假定的)单个令牌的数组。这将为您提供一致的界面,无论是数组还是单个对象。

    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"],
        };
    

    【讨论】:

    • 我收到一个错误:Can not convert Array to String.
    • 关于什么?您需要提供更多详细信息。您提供的样本没有出现此类错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多