C# 反序列化 JSON Schema.org 来自网络的食谱
Posted
技术标签:
【中文标题】C# 反序列化 JSON Schema.org 来自网络的食谱【英文标题】:C# Deserialize JSON Schema.org Recipe from web 【发布时间】:2021-07-09 02:55:26 【问题描述】:我正在尝试用 C# 编写一个食谱解析器。我尝试过同时使用 Newtonsoft 和 System.Text.Json。我使用了在线 json 类构建器和 VS2019 Paste 作为 json 类。我已经运行了 Newtonsoft 示例代码。
Schema.org 食谱https://schema.org/Recipe
我正在使用的课程。我真的只对这两个值感兴趣
public class Recipe
public string recipeIngredient get; set;
public string recipeInstructions get; set;
代码 - 此代码运行时没有错误。
var document = File.ReadAllText(@"D:\recipeExample.json");
Recipe recipe = null;
try
recipe = System.Text.Json.JsonSerializer.Deserialize<Recipe>(document);
catch (System.Text.Json.JsonException ex)
Console.WriteLine($"Error parsing : ex");
试图读取值。没有错误或价值。
Console.WriteLine(recipe.recipeIngredient);
我不确定我是否了解如何遍历 schema.org 文档。在生成的类中,我可以看到 Graph 和 Context 我猜是根节点。但是我的实验表明我不需要所有的类——只需要 recipeIngredient 和 recipeInstructions。 这只是一个食谱 - 我想解析一个食谱列表,我相信它们都会有自己的类,所以我正在寻找最通用的方法来获取我正在寻找的 2 个字段值。
编辑
使用 tymtam 的 Json Document 示例,我可以看到那里有一个对象
Console.WriteLine(recipes.Count);
但我无法获取要显示的值
Console.WriteLine(recipes[0].Ingredients.ToString());
Console.WriteLine(recipes[0].Instructions.ToString());
我也尝试用
打印成分foreach (var recipe in recipes)
Console.WriteLine(recipes[0].Ingredients);
但这只会打印对象名称而不是元素
System.Linq.Enumerable+WhereSelectEnumerableIterator
编辑 2:已解决
foreach (var ingredient in recipes[0].Ingredients)
Console.WriteLine(ingredient);
foreach (var instruction in recipes[0].Instructions)
Console.WriteLine(instruction);
【问题讨论】:
能否提供json文件的内容? 一种可能性是配方被一个额外的对象包裹。 "Recipe" : ...
.
抱歉没有看到您的评论 - 这是文件drive.google.com/file/d/1wS-CnbPjyi6N77WB0TxEVrUsTQypR2q3/…
【参考方案1】:
json的相关部分如下:
"@context":"https://schema.org",
"@graph":[
(...)
"@type":"Recipe",
"recipeIngredient":[
"12 oz rice noodles",
(...)
],
"recipeInstructions":[
"@type":"HowToStep",
"text":"Bring ...",
"name":"Bring ...",
"url":"https://..."
,
(...)
],
(...)
System.Text.Json 和 JsonDocument
using (JsonDocument doc = JsonDocument.Parse(json))
JsonElement root = doc.RootElement;
JsonElement graph = root.GetProperty("@graph");
var recipes = graph.EnumerateArray()
.Where(g => g.GetProperty("@type").GetString() == "Recipe")
.Select(r => new
Ingredients = r.GetProperty("recipeIngredient").EnumerateArray().Select(p => p.GetString()),
Instructions = r.GetProperty("recipeInstructions").EnumerateArray().Select(p => new
@type = p.GetProperty("@type").GetString(),
text = p.GetProperty("text").GetString(),
name = p.GetProperty("name").GetString(),
url = p.GetProperty("url").GetString(),
)
)
.ToList();
System.Text.Json 和类
class Root
[JsonPropertyName("@context")]
public string Context get; set;
[JsonPropertyName("@graph")]
public List<Element> Graph get; set;
class Element
[JsonPropertyName("@type")]
public string Type get;set;
public List<string> RecipeIngredient get; set;
public List<Instruction> RecipeInstructions get; set;
class Instruction
public string Text get; set;
var options = new JsonSerializerOptions
PropertyNameCaseInsensitive = true,
;
var x = JsonSerializer.Deserialize<Root>(json, options);
var recipes = x.Graph.Where( o => o.Type == "Recipe");
Json.Net
请看官方Querying JSON with LINQ或Querying JSON with SelectToken。
这是一个带有查询和类的混合搭配解决方案:
using Newtonsoft.Json.Linq;
JObject o = JObject.Parse(json);
JToken t = o.SelectToken("$.@graph[?(@.@type == 'Recipe')]");
var recipe = t.ToObject<Recipe>();
(...)
class Recipe
public List<string> RecipeIngredient get; set;
public List<Instruction> RecipeInstructions get; set;
class Instruction
public string Text get; set;
对于多个配方,请使用 SelectTokens。
【讨论】:
谢谢@tymtam 你能告诉我如何枚举列表吗?我已经运行了前两个示例而没有错误,但无法确定将列表打印到控制台的内容/方式。 你可以序列化回来:Console.WriteLine(JsonSerializer.Serialize(recipes, new JsonSerializerOptions WriteIndented = true));
【参考方案2】:
我认为您的问题是您正在一次读取整个文件。因此反序列化方法可能无法分配单个值,因为您试图从所有文本中解析一个变量
如果您使用 System.Text.Json 你应该可以在你的类中设置 2 个属性,
public string recipeIngredient get; set;
public string recipeInstructions get; set;
public override string ToString() => JsonSerializer.Serialize<Recipe>(this);
然后当你反序列化时你可以做这样的事情
public list<Recipe> getRecipe()
using (var jsonFileReader = File.OpenText(filename))
return JsonSerializer.Deserialize<Recipe>(jsonFileReader.ReadToEnd());
有了这个,您应该能够创建一个类型为您的食谱类的列表。然后您应该可以参考该列表。
(编辑)
我忘了提到它,但你可能想调用它,这样做你会说
list<Recipe> recipeList = getRecipe();
然后您只需遍历列表,每个对象都会有一个“.recipeIngredient”和“.recipeInstructions”,您可以使用它们来格式化输出
【讨论】:
感谢您的回答,但我不明白您的建议。我复制了你的代码 List以上是关于C# 反序列化 JSON Schema.org 来自网络的食谱的主要内容,如果未能解决你的问题,请参考以下文章
jQuery/ASMX:在 C# 中反序列化 JSON 时出错