#c# #linq
Вопрос:
Я пытаюсь спроецировать плоский источник данных в объект, который может быть сериализован непосредственно в JSON с помощью Newtonsoft.Json. Я создал небольшую программу в Linqpad с воображаемым обзором запасов в качестве теста. Запрошенный результат выглядит следующим образом:
- Название сайта
- Инвентарное наименование
- Название продукта
- Вес
- Единицы
- Инвентарное наименование
- Продукт (и т. Д.)
- Инвентарное наименование
Ни за что на свете я не могу заставить его иметь только одно «Имя сайта» в качестве единственного корневого объекта. Я хочу, чтобы список содержимого внутри инвентаря внутри сайта, но он всегда выглядит так:
Как я могу отличить «Сайт» от коллекции «инвентаря», у каждого из которых есть коллекция «продуктов»?
Мой фактический источник данных — это таблица базы данных, и она напоминает структуру моего тестового объекта-и это то, что она есть.
Тестовый код в Linqpad: (обратите внимание, что он ссылается на Newtonsoft.Json)
void Main()
{
var contents = new List<DatabaseRecord>()
{
new DatabaseRecord{Product="Autoblaster", Inventory="Hull 5", Site="Death star", Units=20,Weight=500},
new DatabaseRecord{Product="E11 Blaster Rifle", Inventory="Hull 5", Site="Death star", Units=512,Weight=4096},
new DatabaseRecord{Product="SWE/2 Sonic Rifle", Inventory="Hull 1", Site="Death star", Units=20,Weight=500},
new DatabaseRecord{Product="Relby v10 Micro Grenade Launcher", Inventory="Hull 5", Site="Death star", Units=20,Weight=500},
new DatabaseRecord{Product="T-8 Disruptor", Inventory="Hull 1", Site="Death star", Units=20,Weight=500},
new DatabaseRecord{Product="E11 Blaster Rifle", Inventory="Hull 2", Site="Death star", Units=50,Weight=1200}
};
var inventorycontent = from row in contents
group row by row.Site into sites
orderby sites.Key
select from inventory in sites
group inventory by inventory.Inventory into inventories
orderby inventories.Key
select new
{
site = sites.Key,
inventory = inventories.Key,
lines = inventories.Select(i => new { i.Product, i.Weight, i.Units })
};
contents.Dump();
inventorycontent.Dump();
JsonConvert.SerializeObject(inventorycontent, Newtonsoft.Json.Formatting.Indented).Dump();
}
// Define other methods and classes here
class DatabaseRecord
{
public string Product { get; set; }
public string Inventory { get; set; }
public string Site { get; set; }
public int Units { get; set; }
public double Weight { get; set; }
}
Вывод JSON:
[
[
{
"site": "Death star",
"inventory": "Hull 1",
"lines": [
{
"Product": "SWE/2 Sonic Rifle",
"Weight": 500.0,
"Units": 20
},
{
"Product": "T-8 Disruptor",
"Weight": 500.0,
"Units": 20
}
]
},
{
"site": "Death star",
"inventory": "Hull 2",
"lines": [
{
"Product": "E11 Blaster Rifle",
"Weight": 1200.0,
"Units": 50
}
]
},
{
"site": "Death star",
"inventory": "Hull 5",
"lines": [
{
"Product": "Autoblaster",
"Weight": 500.0,
"Units": 20
},
{
"Product": "E11 Blaster Rifle",
"Weight": 4096.0,
"Units": 512
},
{
"Product": "Relby v10 Micro Grenade Launcher",
"Weight": 500.0,
"Units": 20
}
]
}
]
]
Предложенный правильный образец вывода:
{
"sites":[{
"site": "Death star",
"inventories":[
{
"name":"Hull 1",
"lines":[{
"Product": "SWE/2 Sonic Rifle",
"Weight": 500.0,
"Units": 20
},
{
"Product": "T-8 Disruptor",
"Weight": 500.0,
"Units": 20
}]
},
{
"name":"Hull 2",
"lines":[{
"Product": "SWE/2 Sonic Rifle",
"Weight": 500.0,
"Units": 20
}]
}
]
},
{"site": "Other site",
"inventories":[
{
"name":"Hull 1",
"lines":[{
"Product": "SWE/2 Sonic Rifle",
"Weight": 500.0,
"Units": 20
}]
}]
}]
}
Комментарии:
1. Вместо использования анонимного типа и группировок… Я бы использовал группировки и словари.
2. Можете ли вы показать правильный образец вывода JSON?
3. @NetMage Добавил пример вывода JSON
4. Я немного изменил свой ответ, чтобы соответствовать вашему выходу json
Ответ №1:
Хорошо, у меня есть решение с использованием словарей, которое все правильно сгруппирует:
//first get everything properly grouped with dictionaries
var result = contents
.GroupBy(x => x.Site)
.ToDictionary(g => g.Key, g => g
.GroupBy(i => i.Inventory)
.ToDictionary(i => i.Key, i => i
.Select(a => new
{
Product = a.Product,
Weight = a.Weight,
Units = a.Units
})
.ToList()));
//project to a new object that matches your desired json
var formattedResult = new
{
sites = (from r in result
select new
{
site = r.Key,
inventories = (from i in r.Value select new { name = i.Key, lines = i.Value }).ToList()
}).ToList()
};
Это выходной json:
{
"sites": [
{
"site": "Death star",
"inventories": [
{
"name": "Hull 5",
"lines": [
{
"Product": "Autoblaster",
"Weight": 500.0,
"Units": 20
},
{
"Product": "E11 Blaster Rifle",
"Weight": 4096.0,
"Units": 512
},
{
"Product": "Relby v10 Micro Grenade Launcher",
"Weight": 500.0,
"Units": 20
}
]
},
{
"name": "Hull 1",
"lines": [
{
"Product": "SWE/2 Sonic Rifle",
"Weight": 500.0,
"Units": 20
},
{
"Product": "T-8 Disruptor",
"Weight": 500.0,
"Units": 20
}
]
},
{
"name": "Hull 2",
"lines": [
{
"Product": "E11 Blaster Rifle",
"Weight": 1200.0,
"Units": 50
}
]
}
]
}
]
}
Как вы можете видеть, я группирую по сайту, затем делаю это в словаре, который затем группирую по инвентарю и делаю это в другом словаре со списком продуктов в качестве значений.
Таким образом, в основном результатом является
Dictionary<string, Dictionary<string, List<Products>>>
Ключами словаря являются Сайт, Название инвентаря.
Я использую этот словарь GroupBy — > > > каждый раз, когда мне приходится создавать иерархический json, подобный этому.
Комментарии:
1. Большое вам спасибо! Это отличное решение, я никогда не рассматривал возможность использования словарей для этого. Однако, что делать, если вы хотите явно назвать значения? Итак, вместо «Звезды смерти»:{ (…) } вам нужно: «Сайт»: {«Имя»: «Звезда смерти», «Описи» :[ (…) ] и т.д. Другими словами, более тесно связано с выводом, который я имею в своем вопросе? Кроме того, в вашем примере есть небольшая ошибка: при последнем выборе используется i , но это имя уже используется в этой области. Я изменил его на а , и все работало нормально.
2. @GeirR Я добавил небольшой фрагмент кода и обновленный ввод json. Я просто спроецировал словари в объект, соответствующий вашему желаемому json.