2

In Unity 5.4, I have a JSON file I can successfully parse via JsonUtility.FromJson when I fetch it locally from StreamingAssets, but fetching that same same file via WWW throws an error (ArgumentException: JSON parse error: Invalid value.) when I fetch it from a remote server.

I can output both the local and remote (jsonString) files just prior to parsing by JsonUtility, and they are identical (I've even validated each in a JSON validator.)

Here's the code I'm using to retrieve and parse the remote JSON:

    void Prime(){
    string url = "https:content_url.json";
    WWW www = new WWW(url);
    StartCoroutine(WaitForContentJSON(www));
}

IEnumerator WaitForContentJSON(WWW contentData)
{
    yield return contentData;

    // check for errors
    if (contentData.error == null)
    {
        ParseJSON(contentData.text);

    } else {
        Debug.Log("WWW Error: "+ contentData.error);
    }    
}

void ParseJSON(string jsonString){
    var ac = JsonUtility.FromJson<ArticlesCollection>(jsonString);
}

The error is thrown inside ParseJSON when invoking JsonUtility.FromJson

Any help much appreciated.

EDIT: Adding JSON per @Programmer's request

JSON returned from local file via File.ReadAllText:

{
  "articles": [ {
    "articleID": "1",
    "title": "Life & Death at the Mexican Border",
    "byline": "Part 1 of Life and Death...",
    "longDescription": "Part 1 of Life and Death...",
    "imageURL": "http://foo.jpg",
    "videoURL": "http://foot.mp4",
    "sceneAssetBundle": "scene_bundle_1",
    "sceneName": "scene_1",
    "featured": true,
        "duration": "7:12",
        "videoSize": "625"
  }, {
    "articleID": "2",
    "title": "Lake Mead",
    "byline": "The shrinking water....",
    "longDescription": "Welcome...",
    "imageURL": "http://vfoo.jpg",
    "videoURL": "http://food.mp4",
    "sceneAssetBundle": "scene_bundle_2",
    "sceneName": "scene_2",
    "featured": true,
        "duration": "1:45",
        "videoSize": "151"
  }, {
    "articleID": "3",
    "title": "Visi...",
    "byline": "Experience...",
    "longDescription": "Experience...",
    "imageURL": "http://foo.jpg",
    "videoURL": "http://foo.mp4",
    "sceneAssetBundle": "scene_bundle_2",
    "sceneName": "scene_2",
    "featured": false,
        "duration": "5:46",
        "videoSize": "478"
  } ]
}

JSON returned from remote (S3):

{
  "articles": [ {
    "articleID": "1",
    "title": "Life & Death at...",
    "byline": "Part 1 of...",
    "imageURL": "http:foo.jpg",
    "videoURL": "http://foo.mp4",
    "featured": true,
        "duration": "7:12",
        "videoSize": "625"
  }, {
    "articleID": "2",
    "title": "Lake Mead",
    "byline": "The...",
    "longDescription": "Welcome...",
    "imageURL": "http://foo.jpg",
    "videoURL": "http://foo.mp4",
    "featured": true,
        "duration": "1:45",
        "videoSize": "151"
  }, {
    "articleID": "3",
    "title": "Visit",
    "byline": "Experience...",
    "longDescription": "Experience the...",
    "imageURL": "http:foo.jpg",
    "videoURL": "http://foo.mp4",
    "featured": false,
        "duration": "5:46",
        "videoSize": "478"
  } ]
}

Again, I've validated both of these JSON files in a validator, and again the JsonUtility.FromJson call works fine when passed the JSON fetched locally but errors when passed the JSON from the remote source fetched via WWW

And, per @dbc's request I'm posting the body of my ArticlesCollection and Articles wrapper classes into/against(?) which the JSON is parsed. But again, this works fine when fetching the JSON locally, so I don't suspect there's an issue in these files.

ArticlesCollection:

using UnityEngine;

[System.Serializable]
public class ArticlesCollection
{
    public Article[] articles;
}

Articles:

using UnityEngine;

[System.Serializable]
public class Article
{
    public string title;
    public int articleID;
    public string byline;
    public string longDescription;
    public string imageURL;
    public string experienceURL;
    public bool featured;
    public string duration;
    public string experienceSize;

    public string sceneAssetBundle;
    public string sceneName;
}
Shannon Perkins
  • 353
  • 4
  • 12
  • Put `Debug.Log(jsonString)` then post both json data here. Label them as json that works and json that doesn't work. Also post the complete class of `ArticlesCollection`. This will help determine the problem. – Programmer Aug 06 '16 at 08:54
  • Please share your JSON. Is your outer JSON container an array? I.e. a comma-delimited sequence of values surrounded by `[` and `]`? (The name `ArticlesCollection` suggests it is.) Because `JsonUtility.FromJson` apparently does not support arrays. For some workarounds see [Unity C#: how to convert an Array of a class into JSON](https://stackoverflow.com/questions/38390274) or [Deserialization of JSON using MiniJSON in Unity C#](https://stackoverflow.com/questions/36239705) – dbc Aug 06 '16 at 13:56
  • @Programmer thank you for responding. I've added the info you've both requested to the post. FYI, I found this thread http://answers.unity3d.com/questions/844423/wwwtext-not-reading-utf-8-text.html, which suggests WWW.text or some other part of the remote fetching process adds additional bytes to the body of the results, and that using WWW.bytes and chopping off some bits may be the answer. – Shannon Perkins Aug 06 '16 at 22:35

2 Answers2

4

Since you are using Unity 5.4, you shouldn't be using WWW for web requests. UnityWebRequest replaced WWW and it solves that problem of 3 extra bytes. There are just many other reasons to use it such as support for https.

IEnumerator WaitForRequest(string url)
{
    UnityWebRequest www = UnityWebRequest.Get(url);
    yield return www.Send();
    if (www.isError)
    {
        Debug.Log("Error: " + www.error);
    }
    else
    {
        Debug.Log("Downloaded: " + www.downloadHandler.text);
        // byte[] results = www.downloadHandler.data;

        ArticlesCollection article = JsonUtility.FromJson<ArticlesCollection>(www.downloadHandler.text);
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thanks Programmer. UnityWebRequest looks like a much better implementation than the old WWW class, but it's throwing the same error in the same way :( Guess I'll stick with my byte chopping hack and explore UnityWebRequest next time I need to fetch something. Thanks for turning me on to the new awesome. – Shannon Perkins Aug 07 '16 at 23:46
  • I believe that UnityWebRequest fixed that.You can also use the `www.downloadHandler.data`. You are welcome. – Programmer Aug 08 '16 at 00:00
  • @Programmer in my case, `var player = JsonHelper.FromJson(www.downloadHandler.text);` returns null when `www.downloadHandler.text` has this value `{"user":"luzan@gmail.com","token":"tokenvalue"}` – Luzan Baral Apr 17 '17 at 07:53
  • Your json is not array so you don't need to use the `JsonHelper` class. Just use `JsonUtility.FromJson`. Also, paste that json [here](http://json2csharp.com/) to see what your json class should look like. – Programmer Apr 17 '17 at 07:58
  • Thanks for reply, I did what you said, and also created json class from the link. and did `Player user = JsonUtility.FromJson(www.downloadHandler.text);` or `var user = ...`, but on both cases `user.user` and `user.token` are empty. – Luzan Baral Apr 17 '17 at 08:24
  • Make sure to look [here](http://stackoverflow.com/a/36244111/3785314) under **3.TROUBLESHOOTING JsonUtility**. If that did not help you, please create new question with the class and json you are getting. I will take a look at it. – Programmer Apr 17 '17 at 08:28
  • 1
    Okay, Thanks. It worked after I removed {get; set;}. – Luzan Baral Apr 17 '17 at 08:34
  • I know it's pretty old post, but I have the same issue using UnityWebRequest. The 3 bytes hack fixed it. I'm using Newtonsoft json. – Nikita Fedorov Jul 12 '22 at 14:26
1

So the answer was as indicated in this post, http://answers.unity3d.com/questions/844423/wwwtext-not-reading-utf-8-text.html

This is a verified fix for my issue.

The issue seems to be that there are exactly 3 extra bytes on the head of the response. The fix is to use WWW.bytes instead of WWW.text, then slice off the extra 3 bytes. Thusly,

string jsonString;
jsonString = System.Text.Encoding.UTF8.GetString(contentData.bytes, 3, contentData.bytes.Length - 3);

It's particularly strange given ALL the documentation I could find indicated using WWW.text. Seems like Unity should add a data property to WWW that would solve this problem.

Shannon Perkins
  • 353
  • 4
  • 12