问题
I'm a novice C# dev and newer to Linq, but I'm trying to figure out how to get the nested child objects from a Linq query into an object. I can get the main/root objects.
This is my code so far and the Json file I'm using. Note, I manually removed some text from the Json if it happens to appear malformed.
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.processJson();
Console.ReadLine();
}
public void processJson()
{
JObject jobject = JObject.Parse(this.getSampleOrderJsonText());
// QUESTION IS IN THIS BLOCK OF CODE
var orders =
from p in jobject["resource"]["items"].Children()["resource"]
select new orderHeader
{
orderNo = (string)p["orderNo"],
status = (string)p["status"]
// How do I fill "lines" and its child object "lines.lineDetails"?
// lines =
};
foreach (orderHeader orderList in orders)
{
Console.WriteLine(orderList.orderNo + " " + orderList.status);
}
}
public class orderHeader
{
public string orderNo { get; set; }
public string status { get; set; }
public List<orderLine> lines { get; set; }
}
public class orderLine
{
public string sku { get; set; }
public int quantity { get; set; }
public List<orderLineDetail> lineDetails { get; set; }
}
public class orderLineDetail
{
public int productId { get; set; }
public string serialNumber { get; set; }
}
public string getSampleOrderJsonText()
{
return "{Entire Json Text}"; // This returns all the JsonText
}
}
Here is my (edited) Json:
{
"status": 200,
"message": "Successful",
"resource": {
"offset": 0,
"total": 1,
"previous": null,
"next": null,
"items": [{
"resource": {
"orderNo": "#6255.1",
"lastUpdatedDate": "2016-01-21T17:39:36-08:00",
"status": "completed",
"vendorId": null,
"vendorExternalId": null,
"id": 153357642,
"items": {
"resource": {
"offset": 0,
"total": 3,
"previous": null,
"next": null,
"items": [{
"resource": {
"sku": "796430390315",
"quantity": 2,
"serialNumbers": {
"resourceLocation": null,
"resource": {
"offset": 0,
"total": 2,
"previous": null,
"next": null,
"items": [{
"resourceLocation": null,
"resource": {
"orderId": 153357642,
"productId": 3525462,
"serialNumber": "T8-0139062"
}
}, {
"resourceLocation": null,
"resource": {
"orderId": 153357642,
"productId": 3525462,
"serialNumber": "T8-0139063"
}
}]
}
},
"productId": 3525462,
"orderId": 153357642,
"ordered": 2,
"shipped": 1
}
}, {
"resource": {
"sku": "796430390322",
"quantity": 2,
"commercialInvoiceValue": 0,
"serialNumbers": {
"resourceLocation": null,
"resource": {
"offset": 0,
"total": 2,
"previous": null,
"next": null,
"items": [{
"resourceLocation": null,
"resource": {
"orderId": 153357642,
"productId": 3525472,
"serialNumber": "T8-0140454"
}
}, {
"resourceLocation": null,
"resource": {
"orderId": 153357642,
"productId": 3525472,
"serialNumber": "T8-0140478"
}
}]
}
},
"productId": 3525472,
"orderId": 153357642,
"ordered": 2,
"shipped": 1
}
}, {
"resourceLocation": null,
"resource": {
"sku": "796430390346",
"quantity": 1,
"commercialInvoiceValue": 0,
"serialNumbers": {
"resourceLocation": null,
"resource": {
"offset": 0,
"total": 1,
"previous": null,
"next": null,
"items": [{
"resourceLocation": null,
"resource": {
"orderId": 153357642,
"productId": 3525482,
"serialNumber": "T8-0141520"
}
}]
}
},
"productId": 3525482,
"orderId": 153357642,
"ordered": 1,
"shipped": 1
}
}]
}
}
"options": {
"resourceLocation": null,
"resource": {
"warehouseId": 13,
"warehouseRegion": "CHI",
"carrierCode": "FDX"
}
}
"shipTo": {
"resource": {
"email": "none@nowhere.com",
"name": "First Last",
"company": "Company, Inc",
"address1": "123 South Street",
"address2": "",
"address3": "",
"city": "Chicago",
"state": "IL",
"postalCode": "60652",
"country": "US",
"phone": "5555551234"
}
}
"pricing": {
"resourceLocation": null,
"resource": {
"shipping": 20.76,
"packaging": 0.88,
"insurance": 9,
"handling": 5.25,
"total": 35.89
}
}
}
}]
}
}
EDIT: WORKING CODE! Is this the best way to do it? What if a child-element doesn't exist?
public void processJson()
{
JObject jobject = JObject.Parse(this.getSampleOrderJsonText());
var orders =
from p in jobject["resource"]["items"].Children()["resource"]
select new orderHeader
{
orderNo = (string)p["orderNo"],
status = (string)p["status"],
lines = (
from nestedChildren in p["items"]["resource"]["items"].Children()["resource"]
select new orderLine
{
sku = (string)nestedChildren["sku"],
quantity = (int)nestedChildren["quantity"]
})
};
foreach (orderHeader orderList in orders)
{
Console.WriteLine(orderList.orderNo + " " + orderList.status);
if (orderList.lines != null)
{
foreach (orderLine line in orderList.lines.ToList())
{
Console.WriteLine("-->{0}: {1}", line.sku, line.quantity);
}
}
}
}
回答1:
The question if there is a better way to write a certain query certainly almost borders the philosophical. Are the ways to do it differently though? Yes, and lots of it!
The select/from syntax of linq is heavily inspired by SQL of course and as a declarative paradigm it has a number of advantages and disadvantages for instance compared to a purely imperative C-like query.
SQl-style Linq:
- fewer instructional overhead (you don't care that much about datatype definitions and procedure)
- they can provide a more straight-forward look over the data that is queried
- especially query nesting CAN lead to horrific and inefficient statements as they quickly get computationally complex
extension method Linq:
Something like:
var MyQuery = MyDataStructure.Where( data => data.property == mySearchProperty )
.Distinct()
.Select( data => data.CustomerID );
- integrates more easily and unobstrusively into a C-style method
- superbly suited for quick statements that would produce much overhead if written in C-style
- can get wildy inefficient if done wrong
C-style query:
- typically consists of a number of nested for loops
- provides the most control over HOW the data is actually queried
- quickly produces a lot of overhead for variable and structural definitions
- Can be a lot more efficient if you want to add instructions during the query
- Can be extremely efficient if you know better than the compiler how to parallelize it (for instance with Parallel.Foreach)
- Usually less easy to read as the desired data pattern is hidden deeper in the structure as opposed to a sql-like query
Annother method that has becomely popular in the cloud for very big data and very high parallization is NO-SQL syntax; however this is a topic for its own.
For you specific case I have to admit, I don't really understand how your data is structured - especially
p["items"]["resource"]["items"].Children()["resource"]
confuses me and seems rather unusual. But as long as you get the data you want and your code is readable and maintainable you can't really go wrong here. Unless you have specific conditions like huge amounts of data or time concerns then your query seems perfectly fine to me.
来源:https://stackoverflow.com/questions/35045782/using-linq-to-select-into-an-object-with-children-from-json