0
\$\begingroup\$

(I posted a very similar question here recently and the response I received was helpful. But I have been advised to start a new question, and here it is.)

I am developing a Unity game which includes a multiple-choice quiz. I've placed what I believe is a properly-formatted .json file (the quiz question, four answer choices, the correct answer, and an explanation for the answer) (which I checked with a .json formatter) in the StreamingAssets folder in the Unity project. I've created a custom class for the question, the four answer choices, the correct answer and the explanation of the answer. I am trying to load the entire contents of the .json file into an array, "questions," then copy that array into another array, "questionsCopy." To avoid repeating questions, as a question is used during the gameplay, I will remove it (and its corresponding answer choices, correct answer and explanation) from the questionsCopy array. (I have not yet developed the GUI to show the question, answer choices and eventually the correct answer and its explanation.)

Here are the updated relevant sections of what I've coded so far:

1) The REVISED custom class:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Linq; // LINQ (Language Integrated Query). The most common uses for LINQ statements tend to be sorting, searching, and filtering (though there’s plenty more you can do with it).
using System.IO;



// ******************************************************************
// See: https://gamedev.stackexchange.com/questions/166759/trying-to-load-json-questions-and-answers-into-a-multiple-choice-quiz-game-in-u
[System.Serializable]
public class QuestionCollection
{
    public QuestionChoicesAnswerHintExplanation[] questions;
}
// ******************************************************************


[System.Serializable]
public class QuestionChoicesAnswerHintExplanation : MonoBehaviour {

    public string[] question; // An array of strings (rather than just a string) so that line spacing, which doesn't play well with .json, could be preserved
    public string[] answerChoices;
    public int correctAnswerIndex;
    public string hint;
    public string explanation;

}

2) From the REVISED GameController:

    public QuestionCollection questionCollection; // See: https://gamedev.stackexchange.com/questions/166759/trying-to-load-json-questions-and-answers-into-a-multiple-choice-quiz-game-in-u

    public QuestionChoicesAnswerHintExplanation[] questions; // This is the fixed size array of the questions, answer choices, answers, hints, and explanations.
    private static List<QuestionChoicesAnswerHintExplanation> questionsCopy; // This is the resizeable list (originally a copy of the array). The size of this list will diminish as we remove items after presenting each question.
    private QuestionChoicesAnswerHintExplanation currentQuestion; // The current question drawn at random.
    private int randomIndex; // the index used to pick the random question.

    string path; // The path to the .json file storing the questions, answer choices, correct answer index, hints, explanations.
    string jsonString; // The contents of the .json file.

In the REVISED Start() in the GameController:

    path = Application.streamingAssetsPath + "/questionsAndAnswers.json";
    jsonString = File.ReadAllText(path);

    questionCollection = JsonUtility.FromJson<QuestionCollection>(jsonString); // questionCollection contains an array of question-answer choices-answer-hint-explanation objects
    questions = questionCollection.questions;


    //questions = JsonUtility.FromJson<QuestionChoicesAnswerExplanation[]>(jsonString);

    Debug.LogWarning("questionCollection:");
    Debug.LogWarning(questionCollection);

    Debug.LogWarning("Questions:");
    Debug.LogWarning(questions);

    if (questionsCopy == null || questionsCopy.Count == 0) // A list with zero elements isn't always null.
    {
        // questionsCopy = questions.ToList<QuestionChoicesAnswerExplanation>(); // <---- That was incorrect.
questionsCopy = questions.ToList<QuestionChoicesAnswerHintExplanation>();

        Debug.LogWarning("questionsCopy:");
        Debug.LogWarning(questionsCopy);
        Debug.LogWarning("questionsCopy.Count:");
        Debug.LogWarning(questionsCopy.Count);
        Debug.LogWarning("questionsCopy[0]:");
        Debug.LogWarning(questionsCopy[0]);
        Debug.LogWarning("questionsCopy[1]:");
        Debug.LogWarning(questionsCopy[1]);
        Debug.LogWarning("questionsCopy[2]:");
        Debug.LogWarning(questionsCopy[2]);

    }

    PickRandomQuestion();

Further down the GameController, here is the REVISED PickRandomQuestion():

    public void PickRandomQuestion()
{
    randomIndex = Random.Range(0, questionsCopy.Count);
    currentQuestion = questionsCopy[randomIndex];
    questionsCopy.RemoveAt(randomIndex);
}

3) Here are the contents of the .json file in the StreamingAssets folder:

    {
    "questions":

    [

{
     "question": ["Trinculo admits 'I have been in such a pickle...' in this comedy:"],
     "answerChoices": ["'The Tempest'", "'Henry 4, Part 1'", "'Henry 5'", "'The Merry Wives of Windsor'"],
     "correctAnswerIndex": 0,
     "hint": "Hint: Trinculo is stranded on an island.",
     "explanation": "'Henry 4, Part 1,' and 'Henry 5, are both histories. To be in 'a pickle' usually means to be in a quandary. A broader view of this passage reveals Trinculo to be drunk: \n\nAlonso: \nAnd Trinculo is reeling ripe: where should they \nFind this grand liquor that hath gilded 'em? \nHow camest thou in this pickle? \n\nTrinculo: \nI have been in such a pickle since I \nsaw you last that, I fear me, will never out of \nmy bones: I shall not fear fly-blowing.' \n\n'The Tempest' 5.1."
     },
     {
     "question": ["In 'Hamlet,' this character remarks: 'Neither a borrower nor a lender be; \nFor loan oft loses both itself and friend, \nAnd borrowing dulls the edge of husbandry. \nThis above all: to thine own self be true,\nAnd it must follow, as the night the day,\nThou canst not then be false to any man:'"],
     "answerChoices": ["Fortinbras", "Gertrude", "Polonius", "The Ghost of Hamlet's Father"],
     "correctAnswerIndex": 2,
     "hint": "Hint: This character is very talkative.",
     "explanation": "The verbose Polonius is speaking to his son Laertes. 'Hamlet' 1.3."
     },
     {
     "question": ["Roger Daltrey, well into his career with The Who, played the roles for both Dromios, the twin servants, in a 1983 BBC production of:"],
     "answerChoices": ["'King John'", "'King Lear'", "'Coriolanus'", "'The Comedy of Errors'"],
     "correctAnswerIndex": 3,
     "hint": "Hint: This play is a comedy.",
     "explanation": "'The Comedy of Errors.'"
     }

     ]
    }

This is not working, however. In the console, I am getting correct values for questionsCopy.Count and for randomIndex. But I am getting null values for questionsCopy[0], questionsCopy1, and questionsCopy[2]. I would appreciate any helpful advice. Thanks.

\$\endgroup\$

bumped to the homepage by Community 3 hours ago

This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.

  • \$\begingroup\$ Are the nulls present in the original questions array, or only after copying it to a list? If you re-order the entries in your json input file, do you get nulls in different places? \$\endgroup\$ – DMGregory Jan 17 at 21:21
  • \$\begingroup\$ @DMGregory: I'm getting "Null" only for questionsCopy[0], questionsCopy1, and questionsCopy[2]. But Debug.LogWarning(questionCollection); and Debug.LogWarning(questions); yield just blank spaces in the console. \$\endgroup\$ – WPWPWP Jan 17 at 21:32
0
\$\begingroup\$

Here is you object declaration:

public class QuestionCollection
{
    public QuestionChoicesAnswerHintExplanation[] questions;
}

And here is what you do:

  questionCollection = JsonUtility.FromJson<QuestionCollection>(jsonString);
  questions = questionCollection.questions;
  questionsCopy = questions.ToList<QuestionChoicesAnswerExplanation>();

I bet you've checked this code many times, so you don't catch the problem. Let me show it as a list:

  • QuestionChoicesAnswerHintExplanation is the type of the collection items.
  • QuestionChoicesAnswerExplanation is the type you ask Linq to cast the items to.

Unless QuestionChoicesAnswerExplanation is a base class of QuestionChoicesAnswerHintExplanation, you'll be getting nulls.

\$\endgroup\$
  • \$\begingroup\$ Thanks @real4x. Good catch but the revised code still yields the same errors. I've edited the code in my original post to show the code corrected with the correct types. \$\endgroup\$ – WPWPWP Jan 17 at 21:12
  • 1
    \$\begingroup\$ Why do you need inheriting from MonoBehaviour? This base class has a huge persistent logic, encapsulated into Unity. I'd not be messing with it if I were you. Try making QuestionChoicesAnswerHintExplanation class a plain C# class. \$\endgroup\$ – real4x Jan 18 at 6:56
  • \$\begingroup\$ Ahh, good catch @real4x! I missed that earlier since I'm often on mobile and the exceptionally-long class name pushed the : MonoBehaviour right out of the scrolling pane. I'm surprised that compiles though - in past experience I've gotten errors when trying to mark a MonoBehaviour as [System.Serializable]. \$\endgroup\$ – DMGregory Jan 18 at 12:29
  • \$\begingroup\$ @real4x OK, I've rewritten the custom class so the initial declaration is now: "public class QuestionChoicesAnswerHintExplanation { " - quotation marks excluded. Now in the console the three "Null" messages questionsCopy[0], questionsCopy[1] and questionsCopy[2] are eliminated but I get blanks where I expected values. \$\endgroup\$ – WPWPWP Jan 20 at 21:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.