C# 在dotnet核心web API中使用现有JSON动态创建新的JSON模型

C# 在dotnet核心web API中使用现有JSON动态创建新的JSON模型,c#,json,C#,Json,这件事我已经坚持了大约一个星期了。我收到一个对我的API的json响应,如下所示,它由问题ID(32个字符长的字符串)和相关答案组成,对于一些问题,答案是预定义的,因此一些答案也有ID。根据问题的不同,可能有多个答案或一个答案 { "b07e33061bd31c8d29095f44d9898826": { "answer": "abc" }, "c4edd6e206973168fb15ced212397197": { "answer": "def" }, "f

这件事我已经坚持了大约一个星期了。我收到一个对我的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 | 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);
}