C# LINQ到JSON-对象或数组的查询

C# LINQ到JSON-对象或数组的查询,c#,linq,json.net,linq-to-json,C#,Linq,Json.net,Linq To Json,我正在尝试获取SEDOL和ADP值的列表。以下是我的json文本: { "DataFeed" : { "@FeedName" : "AdminData", "Issuer" : [{ "id" : "1528", "name" : "ZYZ.A a Test Company", "clientCode" : "ZYZ.A", "s

我正在尝试获取SEDOL和ADP值的列表。以下是我的json文本:

{
    "DataFeed" : {
        "@FeedName" : "AdminData",
        "Issuer" : [{
                "id" : "1528",
                "name" : "ZYZ.A a Test Company",
                "clientCode" : "ZYZ.A",
                "securities" : {
                    "Security" : {
                        "id" : "1537",
                        "sedol" : "SEDOL111",
                        "coverage" : {
                            "Coverage" : [{
                                    "analyst" : {
                                        "@id" : "164",
                                        "@clientCode" : "SJ",
                                        "@firstName" : "Steve",
                                        "@lastName" : "Jobs",
                                        "@rank" : "1"
                                    }
                                }, {
                                    "analyst" : {
                                        "@id" : "261",
                                        "@clientCode" : "BG",
                                        "@firstName" : "Bill",
                                        "@lastName" : "Gates",
                                        "@rank" : "2"
                                    }
                                }
                            ]
                        },
                        "customFields" : {
                            "customField" : [{
                                    "@name" : "ADP Security Code",
                                    "@type" : "Textbox",
                                    "values" : {
                                        "value" : "ADPSC1111"
                                    }
                                }, {
                                    "@name" : "Top 10 - Select one or many",
                                    "@type" : "Dropdown, multiple choice",
                                    "values" : {
                                        "value" : ["Large Cap", "Cdn Small Cap", "Income"]
                                    }
                                }
                            ]
                        }
                    }
                }
            }, {
                "id" : "1519",
                "name" : "ZVV Test",
                "clientCode" : "ZVV=US",
                "securities" : {
                    "Security" : [{
                            "id" : "1522",
                            "sedol" : "SEDOL112",
                            "coverage" : {
                                "Coverage" : {
                                    "analyst" : {
                                        "@id" : "79",
                                        "@clientCode" : "MJ",
                                        "@firstName" : "Michael",
                                        "@lastName" : "Jordan",
                                        "@rank" : "1"
                                    }
                                }
                            },
                            "customFields" : {
                                "customField" : [{
                                        "@name" : "ADP Security Code",
                                        "@type" : "Textbox",
                                        "values" : {
                                            "value" : "ADPS1133"
                                        }
                                    }, {
                                        "@name" : "Top 10 - Select one or many",
                                        "@type" : "Dropdown, multiple choice",
                                        "values" : {
                                            "value" : ["Large Cap", "Cdn Small Cap", "Income"]
                                        }
                                    }
                                ]
                            }
                        }, {
                            "id" : "1542",
                            "sedol" : "SEDOL112",
                            "customFields" : {
                                "customField" : [{
                                        "@name" : "ADP Security Code",
                                        "@type" : "Textbox",
                                        "values" : {
                                            "value" : "ADPS1133"
                                        }
                                    }, {
                                        "@name" : "Top 10 - Select one or many",
                                        "@type" : "Dropdown, multiple choice",
                                        "values" : {
                                            "value" : ["Large Cap", "Cdn Small Cap", "Income"]
                                        }
                                    }
                                ]
                            }
                        }
                    ]
                }
            }
        ]
    }
}
以下是我目前掌握的代码:

var compInfo = feed["DataFeed"]["Issuer"]
.Select(p => new {  
    Id = p["id"],
    CompName = p["name"],
    SEDOL = p["securities"]["Security"].OfType<JArray>() ? 
        p["securities"]["Security"][0]["sedol"] : 
        p["securities"]["Security"]["sedol"]
    ADP = p["securities"]["Security"].OfType<JArray>() ? 
        p["securities"]["Security"][0]["customFields"]["customField"][0]["values"]["value"] : 
        p["securities"]["Security"]["customFields"]["customField"][0]["values"]["value"]
});
但是我现在得到的错误是,
“ExpandoObject”不包含“DataFeed”的定义,并且找不到接受“ExpandoObject”类型的第一个参数的扩展方法“DataFeed”
。注意:我知道这个json文本格式不正确。一个实例有一个数组,另一个是一个对象。我希望代码足够灵活,能够处理这两个实例

[UPDATE2]感谢@dbc迄今为止对我的代码的帮助。我已经更新了上面的json文本,使其与我当前的环境非常匹配。我现在可以得到SEDOLs和ADP代码了。然而,当我试图获得第一个分析员时,我的代码只对对象有效,并为作为数组一部分的分析员生成空值。以下是我当前的代码:

var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
           let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
           where security != null
           select new
           {
               Id = (string)issuer["id"], // Change to (string)issuer["id"] if id is not necessarily numeric.
               CompName = (string)issuer["name"],
               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(),
              Analyst = security["coverage"]
                .DescendantsAndSelf()
                .OfType<JObject>()
                .Select(jo => (string)jo.SelectToken("Coverage.analyst.@lastName"))
                .FirstOrDefault(),
           };
var compInfo=来自提要中的发卡机构。选择令牌(“DataFeed.issuer”)。选择多个(i=>i.ObjectsOrSelf())
让security=issuer.SelectTokens(“securities.security”).SelectMany(s=>s.ObjectsOrSelf()).FirstOrDefault()
哪里有保安!=无效的
选择新的
{
Id=(字符串)颁发者[“Id”],//如果Id不一定是数字,则更改为(字符串)颁发者[“Id”]。
CompName=(字符串)颁发者[“名称”],
SEDOL=(字符串)安全性[“SEDOL”],
ADP=安全性[“自定义字段”]
.后代和自我()
第()类
。其中(o=>(字符串)o[“@name”]==“ADP安全代码”)
.Select(o=>(字符串)o.SelectToken(“values.value”))
.FirstOrDefault(),
分析师=安全[“覆盖范围”]
.后代和自我()
第()类
.Select(jo=>(string)jo.SelectToken(“Coverage.analyst@lastName”))
.FirstOrDefault(),
};
要始终选择第一位分析师,我需要做哪些更改?

使用newtonsoft.json

dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json);

还要注意,您的JSON格式不正确

创建的代理类无法确定安全性的类型,因为在一个实例中它是一个数组,而在另一个实例中它是一个简单的对象

原始答复:

我曾经帮助我获得一些类,现在我可以使用以下类型:

        var obj = JsonConvert.DeserializeObject<RootObject>(json);
        var result = from a in obj.DataFeed.Issuer
                     select new
                     {
                         Sedol = a.securities.Security.sedol,
                         Name = a.name
                     };
var obj=JsonConvert.DeserializeObject(json);
var结果=来自obj.DataFeed.Issuer中的
选择新的
{
Sedol=a.securities.Security.Sedol,
Name=a.Name
};
课程:

     public class Values
    {
        public object value { get; set; }
    }

    public class CustomField
    {
        public string name { get; set; }
        public string type { get; set; }
        public Values values { get; set; }
    }

    public class CustomFields
    {
        public List<CustomField> customField { get; set; }
    }

    public class Security
    {
        public string id { get; set; }
        public string sedol { get; set; }
        public CustomFields customFields { get; set; }
    }

    public class Securities
    {
        public Security Security { get; set; }
    }
    public class Issuer
    {
        public string id { get; set; }
        public string name { get; set; }
        public string clientCode { get; set; }
        public Securities securities { get; set; }
    }

    public class DataFeed
    {
        public string FeedName { get; set; }
        public List<Issuer> Issuer { get; set; }
    }

    public class RootObject
    {
        public DataFeed DataFeed { get; set; }
    }
公共类值
{
公共对象值{get;set;}
}
公共类自定义字段
{
公共字符串名称{get;set;}
公共字符串类型{get;set;}
公共值值{get;set;}
}
公共类自定义字段
{
公共列表自定义字段{get;set;}
}
公共安全
{
公共字符串id{get;set;}
公共字符串sedol{get;set;}
公共自定义字段自定义字段{get;set;}
}
公共类证券
{
公共安全{get;set;}
}
公共类发行人
{
公共字符串id{get;set;}
公共字符串名称{get;set;}
公共字符串clientCode{get;set;}
公共证券{get;set;}
}
公共类数据源
{
公共字符串FeedName{get;set;}
公共列表颁发者{get;set;}
}
公共类根对象
{
公共数据源数据源{get;set;}
}

如果您希望所有SEDOL&ADP值都具有相关的发卡机构Id和CompName,您可以执行以下操作:

var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
               from security in issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf())
               select new
               {
                   Id = (long)issuer["id"], // Change to (string)issuer["id"] if id is not necessarily numeric.
                   CompName = (string)issuer["name"],
                   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(),
               };
产生:

然而,在您目前编写的查询中,您似乎试图只返回每个发行人的第一个SEDOL&ADP。如果这是您真正想要的,请执行以下操作:

var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
               let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
               where security != null
               select new
               {
                   Id = (long)issuer["id"], // Change to (string)issuer["id"] if id is not necessarily numeric.
                   CompName = (string)issuer["name"],
                   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(),
               };
另一方面,由于JSON是多态的(属性有时是对象数组,有时只是对象),我不认为反序列化到类层次结构或
ExpandoObject
会更容易

更新

给定已更新的JSON,您可以使用查找第一个分析人员的姓氏,其中递归搜索运算符处理分析人员可能包含在数组中,也可能不包含在数组中的事实:

var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
               let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
               where security != null
               select new
               {
                   Id = (string)issuer["id"], // Change to (string)issuer["id"] if id is not necessarily numeric.
                   CompName = (string)issuer["name"],
                   SEDOL = (string)security["sedol"],
                   ADP = (string)security["customFields"]
                    .DescendantsAndSelf()
                    .OfType<JObject>()
                    .Where(o => (string)o["@name"] == "ADP Security Code")
                    .Select(o => o.SelectToken("values.value"))
                    .FirstOrDefault(),
                   Analyst = (string)security.SelectTokens("coverage.Coverage..analyst.@lastName").FirstOrDefault(),
               };
var compInfo=来自提要中的发卡机构。选择令牌(“DataFeed.issuer”)。选择多个(i=>i.ObjectsOrSelf())
让security=issuer.SelectTokens(“securities.security”).SelectMany(s=>s.ObjectsOrSelf()).FirstOrDefault()
哪里有保安!=无效的
选择新的
{
Id=(字符串)颁发者[“Id”],//如果Id不一定是数字,则更改为(字符串)颁发者[“Id”]。
CompName=(字符串)颁发者[“名称”],
SEDOL=(字符串)安全性[“SEDOL”],
ADP=(字符串)安全性[“自定义字段”]
.后代和自我()
第()类
。其中(o=>(字符串)o[“@name”]==“ADP安全代码”)
.Select(o=>o.SelectToken(“values.value”))
.FirstOrDefault(),
Analyst=(字符串)security.SelectTokens(“coverage.coverage..Analyst@lastName”).FirstOrDefault(),
};

我会将json反序列化为一个动态ExpandooObject(为简洁起见,或者定义一个类型),然后使用linq。您能否提供一些代码,说明如何使用动态ExpandooObject实现这一点?同样,在这里,您需要调试代码。在foreach循环内设置断点,并检查/展开元素变量。它有什么价值观?该对象内部是否有DataFeed元素?使用调试器,观察整个ExpandooObject的内容。我使用LINQPAD作为IDE。我看到元素>键“DataFeed”,值“ExpandoObject”。这个
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;
    }
}
Console.WriteLine(JsonConvert.SerializeObject(compInfo, Formatting.Indented));
[
  {
    "Id": 1528,
    "CompName": "ZYZ.A a Test Company",
    "SEDOL": "SEDOL111",
    "ADP": "ADPSC1111"
  },
  {
    "Id": 1519,
    "CompName": "ZVV Test",
    "SEDOL": "SEDOL112",
    "ADP": "ADPS1133"
  },
  {
    "Id": 1519,
    "CompName": "ZVV Test",
    "SEDOL": "SEDOL112",
    "ADP": "ADPS1133"
  }
]
var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
               let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
               where security != null
               select new
               {
                   Id = (long)issuer["id"], // Change to (string)issuer["id"] if id is not necessarily numeric.
                   CompName = (string)issuer["name"],
                   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(),
               };
[
  {
    "Id": 1528,
    "CompName": "ZYZ.A a Test Company",
    "SEDOL": "SEDOL111",
    "ADP": "ADPSC1111"
  },
  {
    "Id": 1519,
    "CompName": "ZVV Test",
    "SEDOL": "SEDOL112",
    "ADP": "ADPS1133"
  }
]
var compInfo = from issuer in feed.SelectTokens("DataFeed.Issuer").SelectMany(i => i.ObjectsOrSelf())
               let security = issuer.SelectTokens("securities.Security").SelectMany(s => s.ObjectsOrSelf()).FirstOrDefault()
               where security != null
               select new
               {
                   Id = (string)issuer["id"], // Change to (string)issuer["id"] if id is not necessarily numeric.
                   CompName = (string)issuer["name"],
                   SEDOL = (string)security["sedol"],
                   ADP = (string)security["customFields"]
                    .DescendantsAndSelf()
                    .OfType<JObject>()
                    .Where(o => (string)o["@name"] == "ADP Security Code")
                    .Select(o => o.SelectToken("values.value"))
                    .FirstOrDefault(),
                   Analyst = (string)security.SelectTokens("coverage.Coverage..analyst.@lastName").FirstOrDefault(),
               };