1
votes

I am currently required to create a script that needs to update the metadata of the qna pairs in QnA Maker by matching the questions i have in an excel file. i am currently following the REST API guides:

https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/quickstarts/csharp

i have retrieved the qna pairs from QnA Maker already but since it returned a string value of all the qna information, i need to convert it to JSON and match it against my List of QnA Objects i have retrieved from my excel files.

 public async static Task<string> GetQnAFromQnAMaker()
        {
            string getmethod = "/knowledgebases/{0}/{1}/qna/";
            var method_with_id = String.Format(getmethod, kbid, env);
            var uri = host + service + method_with_id;
            Console.WriteLine("Calling " + uri + ".");
            var response = await Get(uri);

            return response;

        }

i used NewtonSoft Deserialize object

List<FAQs> qnaMakerFaq = JsonConvert.DeserializeObject<List<FAQs>>(qnaFromQnAMaker.Result);

but i get this error:

Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ExcelToQnAMaker.FAQs]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'qnaDocuments', line 2, position 17.'

my returned result string looks something like this...

"{\r\n \"qnaDocuments\": [\r\n {\r\n \"id\": ....

This is my FAQs Class

 public class FAQs
    {
        public List<string> Questions { get; set; }
        public string Answers { get; set; }
        public string Classification { get; set; }
        public string Division { get; set; }
        public int Spid { get; set; }
        public int Kbid { get; set; }
    }
1
as the error clearly says your mapping object FAQs do not have any property named qnaDocuments which contains an array of data.vikscool
You are trying to deserialize to a collection but the root JSON container is an object, so you are getting the same error as in Cannot deserialize the current JSON object (e.g. {“name”:“value”}) into type 'System.Collections.Generic.List`1. Your data model may also be wrong, see How to auto-generate a C# class file from a JSON object string. Any further help would require a minimal reproducible example, specifically the full JSON received.dbc

1 Answers

2
votes

If you take a look at the API documentation for Download Knowledgebase the response you get should look like

{
  "qnaDocuments": [
    {
      "id": 1,
      "answer": "You can change the default message if you use the QnAMakerDialog. See this for details: https://docs.botframework.com/en-us/azure-bot-service/templates/qnamaker/#navtitle",
      "source": "Custom Editorial",
      "questions": [
        "How can I change the default message from QnA Maker?"
      ],
      "metadata": []
    },
    {
      "id": 2,
      "answer": "You can use our REST apis to manage your KB. See here for details: https://westus.dev.cognitive.microsoft.com/docs/services/58994a073d9e04097c7ba6fe/operations/58994a073d9e041ad42d9baa",
      "source": "Custom Editorial",
      "questions": [
        "How do I programmatically update my KB?"
      ],
      "metadata": [
        {
          "name": "category",
          "value": "api"
        }
      ]
    }
  ]
}

The model you're trying to deserialise to should match this structure...

public class KnowledgebaseResponse
{
    public List<KnowledgebaseItem> QnaDocuments { get; set; }
}

public class KnowledgebaseItem
{
    public int Id { get; set; }
    public string Answer { get; set; }
    public string Source { get; set; }
    public List<string> Questions { get; set; }
    public List<KeyValuePair<string, string>> MetaData { get; set; }
}

You can then deserialise the JSON correctly.

If you then want to do some mapping to your final model you'll have to do it as a extra step.