我已经坚持了大约一个星期。我收到如下对我的API的json响应,它由问题ID(32个字符长的字符串)和相关答案组成,对于某些问题,答案是预定义的,因此某些答案也具有ID。根据问题,可能有多个答案或一个答案。
{
"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。
+----+------+---------+--------------+------------+
| 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();
}
如果有人可以提出一种更好的方法来实现这一点,将受到高度赞赏。我认为没有其他方法可以做到这一点。先感谢您。
答案 0 :(得分:2)
您可以创建如下所示的模型:
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; }
}
按照Custom Deserialization using Json.NET所述编写您的JsonConverter。
需要创建自己的JsonConverter来消除对KeyValuePairs的过度使用,从而允许使用接口作为类型。 (接口没有默认构造函数(duh),因此无法与默认JsonConverter一起使用。此外,默认转换器无法决定或不知道存在哪些实现,应将其用于什么json构造)
答案 1 :(得分:2)
我不会在这里用JsonTextReader
来遍历JSON;该类使用起来可能有点麻烦。看起来您已经将所有数据保存在JObject
中(尽管您已将其声明为dynamic
),因此您已经有了一个不错的API来处理数据。
首先要做的是更改jsonObject
参数的声明,以使其为强类型而不是dynamic
。这样一来,您就可以在其上使用System.Linq
方法,并为您提供编译时类型检查和智能感知支持,并且还应该可以提高性能。
public async Task<IActionResult> PostResponses([FromBody] JObject jsonObject)
接下来,让我们创建一个简单的类来表示要从JSON中获取的数据行。
class Row
{
public int Year { get; set; }
public int Quarter { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
现在,我们可以从jsonObject
中提取数据,并创建像这样的行列表:
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
});
}
}
这时,您现在有了一个List<Row>
,其中包含问题中描述的格式的数据。您只需简单地遍历它们,然后将它们插入数据库中即可。
提琴:https://dotnetfiddle.net/fyr06g
如果您需要从数据库中针对这些行检索现有的问题和答案文本,则应避免对每个问题和答案进行单独的查询,因为这样做的效果会很差。而是使用一个查询来获取目标问题,并使用第二个查询来获取目标答案。将每个结果集放入单独的字典中以便于查找,然后可以将它们与最后的行进行匹配。这是代码中的样子:
// 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);
}