C# 在dotnet核心web API中使用现有JSON动态创建新的JSON模型
这件事我已经坚持了大约一个星期了。我收到一个对我的API的json响应,如下所示,它由问题ID(32个字符长的字符串)和相关答案组成,对于一些问题,答案是预定义的,因此一些答案也有ID。根据问题的不同,可能有多个答案或一个答案C# 在dotnet核心web API中使用现有JSON动态创建新的JSON模型,c#,json,C#,Json,这件事我已经坚持了大约一个星期了。我收到一个对我的API的json响应,如下所示,它由问题ID(32个字符长的字符串)和相关答案组成,对于一些问题,答案是预定义的,因此一些答案也有ID。根据问题的不同,可能有多个答案或一个答案 { "b07e33061bd31c8d29095f44d9898826": { "answer": "abc" }, "c4edd6e206973168fb15ced212397197": { "answer": "def" }, "f
{
"b07e33061bd31c8d29095f44d9898826": {
"answer": "abc"
},
"c4edd6e206973168fb15ced212397197": {
"answer": "def"
},
"f35270e30bafb94a30f1b22786f748a6": {
"selectedAnswers": [
"b66c043042586e893a147d4b0ba53bdf",
"85eb345f9ad8035bb8928faa4b2b741d",
"071475576d1925e595c39e47ea00e45c"
]
},
"fc6b41df07db039e3903665e0669f8e9": {
"58ff182f96dd321a24bcd08a982566c6": "11b5da0633d22d584f58f85c21804dbf",
"6fee5fc87f6515467f870015b84d8975": "5467a55ce17aa356898592a60d06964e",
"7281d97f90af65eae568320ce3b1e510": "59e7d9c42190c6882d28c8705c2b3cca",
"a69fe0b53807a01ff8f0c83069c74ecf": "92323316ddacdd7e0db911e12ee9ec20",
"d4e0e900c9c960a9b8cd994ea984c7d6": "dfe0109763b30f5be4055c1097be6648"
},
"60ed2fc37d5207b03ad2a72c6ae56793": {
"answer": "hij"
},
"593dfbd2317debd60e74c321512fe77a": {
"1c99416b1c016fdf0ce0b7c996e402e8": "0e1c73a2846468eef95313d4e5c394d6",
"aabd5d7ceebf0ca04970cf836f8aaa41": "4edea9bc2acd426c04b20ad0f656dfda",
"df9b926b795e8ec31bef4156435c4ab9": "aa17bd8932f47b26caf8bd27aa8b00e9"
},
"fcb5de7c3484c88120c92edf399d17a8": {
"answer": "klm"
},
"0f9d2977e66fe7e6bcfb78659f13f9af": {
"answer": "nop"
},
"92de1c7bae914e1213ecc95dd0a7c8a0": {
"answer": "qrs"
},
"74e7f471011fdbf780f25563f4f92a0b": {
"answer": "tuv"
},
"75fa3e245138f7fadc68083aebab55c2": {
"answer": 5
},
"e41bb071c73d64647e65f1474a12604b": {},
"year": 2019,
"quarter": 1
}
有些问题里面有子问题。就像这件事一样
"593dfbd2317debd60e74c321512fe77a": {
"1c99416b1c016fdf0ce0b7c996e402e8": "0e1c73a2846468eef95313d4e5c394d6",
"aabd5d7ceebf0ca04970cf836f8aaa41": "4edea9bc2acd426c04b20ad0f656dfda",
"df9b926b795e8ec31bef4156435c4ab9": "aa17bd8932f47b26caf8bd27aa8b00e9"
}
我需要对它进行处理,并将其转换为键值对,这样我就可以将其存储在具有类似这种结构的数据库中
+----+------+---------+----------------------------------+----------------------------------+
| Id | Year | Quarter | Question | Answer |
+----+------+---------+----------------------------------+----------------------------------+
| 1 | 2019 | 1 | b07e33061bd31c8d29095f44d9898826 | abc |
| 2 | 2019 | 1 | c4edd6e206973168fb15ced212397197 | def |
| 3 | 2019 | 1 | f35270e30bafb94a30f1b22786f748a6 | b66c043042586e893a147d4b0ba53bdf |
| 4 | 2019 | 1 | f35270e30bafb94a30f1b22786f748a6 | 85eb345f9ad8035bb8928faa4b2b741d |
| 5 | 2019 | 1 | f35270e30bafb94a30f1b22786f748a6 | 071475576d1925e595c39e47ea00e45c |
| 6 | 2019 | 1 | fc6b41df07db039e3903665e0669f8e9 | null |
| 7 | 2019 | 1 | 58ff182f96dd321a24bcd08a982566c6 | 11b5da0633d22d584f58f85c21804dbf |
| 8 | 2019 | 1 | 6fee5fc87f6515467f870015b84d8975 | 5467a55ce17aa356898592a60d06964e |
+----+------+---------+----------------------------------+----------------------------------+
为了使事情更简单,我将进一步总结这个问题
+----+------+---------+--------------+------------+
| Id | Year | Quarter | Question | Answer |
+----+------+---------+--------------+------------+
| 1 | 2019 | 1 | questionId 1 | abc |
| 2 | 2019 | 1 | questionId 2 | def |
| 3 | 2019 | 1 | questionId 3 | answerId 1 |
| 4 | 2019 | 1 | questionId 3 | answerId 2 |
| 5 | 2019 | 1 | questionId 3 | answerId 3 |
| 6 | 2019 | 1 | questionId 4 | null |
| 7 | 2019 | 1 | questionId 5 | answerId 4 |
| 8 | 2019 | 1 | questionId 6 | answerId 5 |
+----+------+---------+--------------+------------+
所以我决定创建一个新的json模型,其结构为
"year" : "2019"
"quarter" : "1"
"question 1" : "answer 1"
"question 2" : "answer 2"
"question 3" : "answer 3"
目前,我尝试循环对象并获取值,但该算法太复杂,并且需要花费时间。在某些情况下,它不能正确地将答案与问题相匹配
public async Task<IActionResult> PostResponses([FromBody] dynamic jsonObject)
{
string answerText = null;
var model = new JObject();
model.Add("Year", jsonObject["year"].ToString());
model.Add("Quarter", jsonObject["quarter"].ToString());
using (var reader = new JsonTextReader(new StringReader("[" + jsonObject + "]")))
{
while (reader.Read())
{
string questionText = null;
if (reader.TokenType == JsonToken.PropertyName )
{
string questionId = reader.Value.ToString();
if (questionId.Length == 32)
{
try
{
// get question corresponding to this question ID
var question = await _context.Questions.FirstOrDefaultAsync(s => s.Id == questionId);
System.Diagnostics.Debug.WriteLine("Question -" + questionId);
questionText = question.Text;
}
catch (Exception)
{
}
}
}
if (reader.TokenType == JsonToken.String || reader.TokenType == JsonToken.Integer)
{
string answerId = reader.Value.ToString();
if (answerId.Length == 32)
{
try
{
// get answer corresponding to this answer ID
var answers = await _context.OfferedAnswers.FirstOrDefaultAsync(s => s.Id == answerId);
answerText = answers.Value;
}
catch (Exception)
{
}
}
else
{
answerText = answerId;
}
}
if (questionText != null && answerText != null)
{
model.Add(questionText, answerText);
}
}
System.Diagnostics.Debug.WriteLine(model.ToString());
}
return Ok();
}
公共异步任务PostResponses([FromBody]动态jsonObject)
{
字符串answerText=null;
var模型=新作业对象();
model.Add(“Year”,jsonObject[“Year”].ToString());
添加(“Quarter”,jsonObject[“Quarter”].ToString());
使用(var reader=newjsontextreader(newstringreader(“[”+jsonObject+“]))
{
while(reader.Read())
{
字符串questionText=null;
if(reader.TokenType==JsonToken.PropertyName)
{
string questionId=reader.Value.ToString();
if(questionId.Length==32)
{
尝试
{
//获取与此问题ID对应的问题
var question=wait_context.Questions.FirstOrDefaultAsync(s=>s.Id==questionId);
System.Diagnostics.Debug.WriteLine(“问题-”+问题ID);
questionText=问题。文本;
}
捕获(例外)
{
}
}
}
if(reader.TokenType==JsonToken.String | | reader.TokenType==JsonToken.Integer)
{
字符串answerId=reader.Value.ToString();
if(answerId.Length==32)
{
尝试
{
//获取与此答案ID对应的答案
var answers=wait _context.OfferedAnswers.FirstOrDefaultAsync(s=>s.Id==answerId);
answerText=answers.Value;
}
捕获(例外)
{
}
}
其他的
{
answerText=answerId;
}
}
if(questionText!=null&&answerText!=null)
{
添加(问题文本、回答文本);
}
}
System.Diagnostics.Debug.WriteLine(model.ToString());
}
返回Ok();
}
如果有人能提出更好的方法,我们将不胜感激。我看不出还有别的办法。先谢谢你 您可以创建如下所示的模型:
public class Submission
{
[JsonConverter(typeof(QuestionConverter))]
List<IQuestion> Questions
}
public interface IQuestion
{
public string Id { get; set; }
}
public class SingleChoiceQuestion : IQuestion
{
public string Id { get; set; }
public string Answer { get; set; }
}
public class MultipleChoiceQuestion : IQuestion
{
public string Id { get; set; }
public List<string> SelectedAnswers { get; set; }
}
// this lets you nest questions inside questions (or just multiple single choice questions)
public class RecursiveQuestion : IQuestion
{
public string Id { get; set; }
public List<IQuestion> Question { get; set; }
}
int year = (int)jsonObject["year"];
int quarter = (int)jsonObject["quarter"];
// Find all the descendant JProperties in the JObject with names having
// a length of 32-- these represent the questions.
var props = jsonObject.Descendants()
.OfType<JProperty>()
.Where(prop => prop.Name.Length == 32);
// Transform the properties into a list of Rows
List<Row> rows = new List<Row>();
foreach (JProperty prop in props)
{
// Create a list of answers for the question
var answers = new List<string>();
// if the property value is a string, this is one of the nested questions
// and the "answer" is actually an ID
if (prop.Value.Type == JTokenType.String)
{
answers.Add((string)prop.Value);
}
// if the property value is an object, we could have 0, 1 or many answers inside
else if (prop.Value.Type == JTokenType.Object)
{
if (prop.Value["answer"] != null) // single answer
{
answers.Add((string)prop.Value["answer"]);
}
else if (prop.Value["selectedAnswers"] != null) // many answers
{
answers.AddRange(prop.Value["selectedAnswers"].Values<string>());
}
else // no answers
{
answers.Add(null);
}
}
// Now create a Row for each answer for this question and add it to the list of rows
foreach (string answer in answers)
{
rows.Add(new Row
{
Year = year,
Quarter = quarter,
Question = prop.Name, // The property name is the question ID
Answer = answer
});
}
}
公共类提交
{
[JsonConverter(类型(问题转换器))]
列出问题
}
公共接口测试
{
公共字符串Id{get;set;}
}
公共类单一选项问题:IQuestion
{
公共字符串Id{get;set;}
公共字符串答案{get;set;}
}
公共类多路复用问题:IQuestion
{
公共字符串Id{get;set;}
选择的公用列表答案{get;set;}
}
//这使您可以将问题嵌套在问题中(或仅多个单选题)
公共类递归问题:IQuestion
{
公共字符串Id{get;set;}
公共列表问题{get;set;}
}
像这里描述的那样编写JsonConverter
需要创建您自己的JsonConverter,以消除KeyValuePairs的极端使用,从而允许将接口用作类型。(接口没有默认构造函数(duh),因此无法与默认JsonConverter一起使用。此外,默认转换器无法决定或知道存在哪些实现,以及应该用于什么json构造)您可以创建如下所示的模型:
public class Submission
{
[JsonConverter(typeof(QuestionConverter))]
List<IQuestion> Questions
}
public interface IQuestion
{
public string Id { get; set; }
}
public class SingleChoiceQuestion : IQuestion
{
public string Id { get; set; }
public string Answer { get; set; }
}
public class MultipleChoiceQuestion : IQuestion
{
public string Id { get; set; }
public List<string> SelectedAnswers { get; set; }
}
// this lets you nest questions inside questions (or just multiple single choice questions)
public class RecursiveQuestion : IQuestion
{
public string Id { get; set; }
public List<IQuestion> Question { get; set; }
}
int year = (int)jsonObject["year"];
int quarter = (int)jsonObject["quarter"];
// Find all the descendant JProperties in the JObject with names having
// a length of 32-- these represent the questions.
var props = jsonObject.Descendants()
.OfType<JProperty>()
.Where(prop => prop.Name.Length == 32);
// Transform the properties into a list of Rows
List<Row> rows = new List<Row>();
foreach (JProperty prop in props)
{
// Create a list of answers for the question
var answers = new List<string>();
// if the property value is a string, this is one of the nested questions
// and the "answer" is actually an ID
if (prop.Value.Type == JTokenType.String)
{
answers.Add((string)prop.Value);
}
// if the property value is an object, we could have 0, 1 or many answers inside
else if (prop.Value.Type == JTokenType.Object)
{
if (prop.Value["answer"] != null) // single answer
{
answers.Add((string)prop.Value["answer"]);
}
else if (prop.Value["selectedAnswers"] != null) // many answers
{
answers.AddRange(prop.Value["selectedAnswers"].Values<string>());
}
else // no answers
{
answers.Add(null);
}
}
// Now create a Row for each answer for this question and add it to the list of rows
foreach (string answer in answers)
{
rows.Add(new Row
{
Year = year,
Quarter = quarter,
Question = prop.Name, // The property name is the question ID
Answer = answer
});
}
}
公共类提交
{
[JsonConverter(类型(问题转换器))]
列出问题
}
公共接口测试
{
公共字符串Id{get;set;}
}
公共类单一选项问题:IQuestion
{
公共字符串Id{get;set;}
公共字符串答案{get;set;}
}
公共类多路复用问题:IQuestion
{
公共字符串Id{get;set;}
选择的公用列表答案{get;set;}
}
//这使您可以将问题嵌套在问题中(或仅多个单选题)
公共类递归问题:IQuestion
{
公共字符串Id{get;set;}
公共列表问题{get;set;}
}
像这里描述的那样编写JsonConverter
需要创建您自己的JsonConverter,以消除KeyValuePairs的极端使用,从而允许将接口用作类型。(接口没有默认构造函数(duh),因此无法与默认的JsonConverter一起使用。此外,默认转换器无法决定或知道存在什么实现,以及应该用于什么json构造)我不会在这里使用
JsonTextReader
来挑选json;该类的使用可能有点麻烦。看起来您已经拥有了JObject
中的所有数据(尽管您已经将其声明为动态
),因此您已经拥有了一个不错的AP
// gather the unique question IDs
var questionIds = rows.Select(r => r.Question).ToList();
// query the database for all the questions referenced in the rows list
// and put the question texts in a dictionary by keyed by question id
var questionDict = _context.Questions
.Where(q => questionIds.Contains(q.Id))
.ToDictionary(q => q.Id, q => q.Text);
// gather the unique answer IDs
var answerIds = rows.Where(r => r.Answer != null && r.Answer.Length == 32)
.Select(r => r.Answer)
.Distinct()
.ToList();
// query the database for all the answers referenced in the rows list
// and put the answer values in a dictionary by keyed by answer id
var answerDict = _context.OfferedAnswers
.Where(a => answerIds.Contains(a.Id))
.ToDictionary(a => a.Id, a => a.Value);
// now we can loop over the rows and look up the question/answer text in the dictionaries
foreach (var row in rows)
{
string questionText = null;
if (!questionDict.TryGetValue(row.Question, out questionText))
questionText = row.Question; // if not found, use the question ID instead
string answerValue = null;
if (row.Answer != null && !answerDict.TryGetValue(row.Answer, out answerValue))
answerValue = row.Answer; // if not found use the answer value from the row instead
Console.WriteLine(questionText + " -- " + answerValue);
}