I'm trying to access a Json script, it is in a domain. The script holds a couple of images, which are hyperlinked. I've tried using WWW to access the script, but the image i received was a picture of a huge red question mark. Clearly I'm going about the wrong way with this. So i'm assuming I'm supposed to decode the json script through unity and then display the image with the ability to see next/previous image by clicking? I'm unfamiliar with Json so how about would i call the script and read the the images it's calling?
This is what my code looks like, the code works since I've tried another non Json domain with just an image- and it works perfectly fine.
void Start ()
{
renderer.material.mainTexture = new Texture2D(4,4, TextureFormat.DXT1, false);
url = "http://hosting.xivmedia.com/JsonHome/JSON/more_games.json";
www = new WWW(url);
StartCoroutine(WaitForSeconds(www));
}
IEnumerator WaitForSeconds(WWW www)
{
yield return www;
www.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.data);
imageLoaded = true;
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
void OnGUI()
{
GUI.DrawTexture(new Rect(20, 80, 100, 100), renderer.material.mainTexture as Texture2D, ScaleMode.StretchToFill);
}
Yes, as you said correctly, you'll need to decode the JSON script first. You'll need a JSON parser to do that. The one I highly recommend is JSON .NET For Unity, but MiniJSON should do just fine.
So, assuming your json is just a list of URLs as so:
[
"http://.....",
"http://....."
]
With JSON.NET you would modify your code as follows:
IEnumerator WaitForSeconds(WWW www)
{
yield return www;
string json = www.text;
List<string> imageUrls = JsonConvert.Deserialize<List<string>>(json);
foreach (string imageUrl in imageUrls)
{
WWW imageWWW = new WWW(imageUrl);
imageWWW.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
if (imageWWW .error == null)
{
Debug.Log("WWW Ok!: " + imageWWW.data);
imageLoaded = true;
}
else
{
Debug.Log("WWW Error: " + imageWWW.error);
}
}
}
If the JSON is more complicated, you can create a class that the JSON can deserialize to. I.e., if the JSON is a list of objects as so:
[
{
"name": "test",
"url" : "http://...."
},
{
"name": "something",
"url" : "http://..."
}
]
Then you would have a simple class
public class ImageData
{
public string name;
public string url;
}
And deserialize the JSON as so
List<ImageData> imageUrls = JsonConvert.Deserialize<List<ImageData>>(json);
foreach (ImageData data in imageUrls)
{
WWW imageWWW = new WWW(data.url);
imageWWW.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
Related
I'm trying to Parse two objects sent via Express to my unity client into c# objects.
Here I am sending the search results of a mongoose.model.find
res.send({account: accountData, score: scoreData})
Over in my Unity client this is the function that grabs the data above from my node.js server. I recieve the information here as the www.downloadHandler.text
using (UnityWebRequest www = UnityWebRequest.Post(hostURL + "Login", loginForm))
{
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log("Error Sending Login Detail: " + www.error);
}
else
{
if (www.responseCode == 200)
{
Debug.Log(www.downloadHandler.text);
thisUser = username;
menu.LoginPassed();
leaderboard.LoadPlayerScoreData(www.downloadHandler.text);
}
else
{
Debug.Log("Login Unsuccessful. Reason: " + www.downloadHandler.text);
}
}
}
}
for reference here is the output of the Debug.Log(www.downloadHandler.text) so we can see what we are working with:
{
"account":[
{
"_id":"62fc40709bc9c241c7f650cd",
"username":"BaggyJ",
"password":"12345",
"email":"#hotmail",
"hhs":0,
"level":1,
"xp":0,
"currency":0,
"premiumCurrency":0,
"__v":0
}
],
"score":[
{
"_id":"62fb4efe6e3a942138405b3e",
"username":"BaggyJ",
"score":420,
"__v":0
}, // ...
]
}
Here I am recieving the information as intended, the account data contains the player account informaton and the score data is an array of the players best scores. So far so good!
The next step obviously, is to parse this information into a usable C# object. I attempted to do this in the same way I had previously accomplished in this project.
I will show you here the solution for this I'm using that does not work, and an example of the solution when it does work.
Firstly, what I have thats not working:
I created two C# serialized classes to represent the information.
[System.Serializable]
public class DataPackage
{
public string account;
public string score;
}
And
[System.Serializable]
public class DataPackages
{
public DataPackage[] dataPackages;
}
Finally, I use the JSON Utility function to parse the data string included above
public DataPackages ParseData(string jsonText)
{
DataPackages dataPackages = JsonUtility.FromJson<DataPackages>("{\"dataPackages\":" + jsonText + "}");
return dataPackages;
}
If I attempt to use the above code the dataPackages.dataPackages object is always null.
The function is meant to result in an object with two strings, the first to represent the account data and the second to represent the score data.
These two string will then be parsed a second time into their final C# objects for use.
It is worth noting here that the parsing tools for account data and score data are built the same way and function as expected. I will include one below for reference:
[System.Serializable]
public class ScorePacket
{
public string username;
public string score;
}
[System.Serializable]
public class ScorePackets
{
public ScorePacket[] scorePackets;
}
public ScorePackets ParseScore(string jsonText)
{
ScorePackets scorePackets = JsonUtility.FromJson<ScorePackets>("{\"scorePackets\":" + jsonText + "}");
return scorePackets;
}
The above code will return an c# object where I can access its data as such: scorePackets.scorePackets[0].score
Any insight into why I cannot accomplish this with DataPackages would be greatly appreciated!
EDIT
So I've gotten it a little closer but not quite.
I have replaced:
DataPackages dataPackages = JsonUtility.FromJson<DataPackages>("{\"dataPackages\":" + jsonText + "}");
With:
DataPackages dataPackages = JsonUtility.FromJson<DataPackages>("{\"dataPackages\":[" + jsonText + "]}");
This successfully creates a C# object with the correct fields HOWEVER both of these field are empty.
Debug.Log(dataPackages.dataPackages[0].score)
Debug.Log(dataPackages.dataPackages[0].account)
Both return empty strings.
Edit#2
We are getting so close I can taste it.
So in addition to the above changes I have also made the following change to my node.js server:
res.send({account: accountData.toString(), score: scoreData.toString()})
Now if I:
Debug.Log(dataPackages.dataPackages[0].score.ToString());
Debug.Log(dataPackages.dataPackages[0].account.ToString());
My output is:
[object Object],[object Object],[object Object],[object Object],[object Object]
[object Object]
.ToString() failed to get the json text for these objects
I figured it out!
I needed to change the way I declared my DataPacket class from containing strings to containing object arrays.
[System.Serializable]
public class DataPackage
{
public AccountPacket[] account;
public ScorePacket[] score;
}
Solved my problems
I've taken over development of an app that is complete minus a bug requiring an internet connection to load images as it doesn't access them from the cache despite an attempt to do so.
Can anyone help me figure out what is going wrong in the following?
public class SpriteCache : Singleton<SpriteCache>
{
Dictionary<string, Sprite> _cache = new Dictionary<string, Sprite>();
public void LoadSprite(string url, Action<Sprite> callback)
{
StartCoroutine(LoadSpriteCoroutine(url, callback));
}
public IEnumerator LoadSpriteCoroutine(string url, Action<Sprite> callback)
{
if (_cache.ContainsKey(url))
{
callback(_cache[url]);
yield break;
}
var www = new WWW(url);
while (!www.isDone)
{
yield return www;
}
if (!string.IsNullOrEmpty(www.error))
{
Debug.LogErrorFormat("Tried to obtain texture at '{0}' but received error '{1}'", url, www.error);
yield break;
}
var texture = www.texture;
if (texture == null)
{
Debug.LogErrorFormat("No texture found at '{0}'!", url);
yield break;
}
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2, texture.height / 2));
_cache[url] = sprite;
callback(sprite);
}
edit:
A further explanation to clarify
var www = new WWW(url)
This grabs the images stored on a server which works, as far as I'm aware after one instance of grabbing the image this should place the item in cache for use later.
I've tried using the following updated method to see if that would fix it.
var www = WWW.LoadFromCacheOrDownload(url, 1)
This resulted in it not working in any capacity and never changing the images from the placeholders.
The first if statement in the "LoadSpriteCoroutine" is supposed to catch if the sprite already exists in the "_cache" Dictionary by checking if there is a key for the url, which there should be after the first running instance with and internet connection
Load image from _cache if its in there:
if (_cache.ContainsKey(url))
{
callback(_cache[url]);
yield break;
}
Add image to _cache if it wasn't previously in there:
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2, texture.height / 2));
_cache[url] = sprite;
So I figured out a solution,
all the other methods of saving images to the cache seemed to fail for iOS, I removed all of the previous "_cache" stuff.
this function for saving worked for me:
//if file doesn't exist then save it for fututre reference
if (!File.Exists(_FileLocation + texName))
{
byte[] bytes;
if (Path.GetExtension(texName) == ".png")
{
bytes = texture.EncodeToPNG();
}
else
{
bytes = texture.EncodeToJPG();
}
File.WriteAllBytes(Application.persistentDataPath + texName, bytes);
}
that section is placed after the "callback(sprite)".
The check for saved items is placed where the old "if(_cache.ContainsKey(url))" section was
texName = Path.GetFileName(texName);
if (File.Exists( _FileLocation + texName))
{
url = _FileLocation + "/"+ texName;
}
please note that the "LoadSpriteCoroutine" now take a extra string argument 'texName' in its calls which is just the a shortened instance of the url which it then cuts to just the file name and extension. If it finds a match then it replaces url with a the persistent file path + the fileName to grab it locally opposed to over the network as normal.
_FileLocation is defined as follows
string _FileLocation;
public void Start()
{
_FileLocation = Application.persistentDataPath;
}
The reason behind referencing it in that method is to save performance from calling the address though application AND if you were working on a cross platform solution then the data path for saving may need to be changed as such you could put a detection if or case statement to change _FileLocation to suit your needs.
I am using Unity3d (c#) with facebook API. I try to show my profile image on the screen when I run it on the unity simulator everything works perfect and I got the image. but when I build a version and try it on a real device I get nothing.
I know that there is a issue with facebook CDN. the result of the request is 302 and WWW class can't handle with redirects so I did something like this (c# is a new language for me):
WWW www = new WWW(url);
yield return www;
if( www.responseHeaders.ContainsKey( "LOCATION" ) ){
var redirection = www.responseHeaders[ "LOCATION" ];
WWW wwwRe = new WWW( redirection );
yield return wwwRe;
callback( wwwRe.texture, userID );
}else{
callback( www.texture, userID );
}
please help me I loose my mind, why all my personal data comes on the device except the image that works perfect in unity. what I did wrong?
thanks.
The solution:
I tried many options to get profile image on the device nothing worked.
finally I upgrade Facebook SDK from 6.2.1 to 6.2.2 and Unity from 5.1 to 5.1.3 remove the app again from the device ( clear all the data ) and it works. it looks like Facebook's issue ( this is not the first time that they release SDK with bugs ).
I accept Umair's answer even that his code have some syntax issues, he really tried to help me and basically his answer is right.
I used this code to test my image:
( hope it will help for someone )
private void getMyProfileData(){
// get profile image.
FB.API( Util.GetPictureURL( "me", 128, 128 ), Facebook.HttpMethod.GET, callBackGetProfilePicture );
}
private void callBackGetProfilePicture( FBResult result ){
// in case if there some error with the image.
if( result.Error != null ){
// call this method again
}
string ImageUrl = Util.DeserializePictureURLString( result.Text );
StartCoroutine(LoadPictureCoroutune( ImageUrl ));
}
IEnumerator LoadPictureCoroutune(string url){
var profilePicRequest = new WWW( (string)url );
yield return profilePicRequest;
if( profilePicRequest.error == null)
{
Texture2D profilePic = profilePicRequest.texture;
// my test image place
Image profileImage = FBavatar.GetComponent<Image>();
profileImage.sprite = UnityEngine.Sprite.Create( profilePic, new Rect(0,0,128,128), new Vector2( 0, 0 ));
}
else
{
Debug.LogError("Error While downloading Picture: " + profilePicRequest.error);
}
}
Use following Code to get Picture data from Facebook API:
FB.API ("/v2.4/me/?fields=picture", Facebook.HttpMethod.GET, RequestResponce);
In RequestResponce method:
void RequestResponce(FBResult result)
{
Debug.Log("requestResponce(): " + result.text);
if (!string.IsNullOrEmpty(result.Error))
{
Debug.Log("FB_API:ReadRequest:Failed:"+result.Error);
}
else
{
var data = SimpleJSON.JSON.Parse(result.Text);
string url = data["picture"]["data"]["url"];
StartCoroutine(LoadPictureCoroutune(url));
}
}
IEnumerator LoadPictureCoroutune(string url)
{
var profilePicRequest = new WWW(url);
Debug.Log("Going to Load Picture ");
yield return profilePicRequest;
if(profilePicRequest.error == null)
{
Texture2D profilePic = profilePicRequest.texture;
if(profilePic != null)
{
// Use ProfilePic to wherever you want to.
//SetPicture(ProfilePic);
}
}
else
{
Debug.LogError("Error While downloading Picture: " + profilePicRequest.error);
}
}
I'm retrieving a json file from my server and then using Json to deserialize the content. How ever I keep receiving this error:
Cannot cast from source type to destination type
I was following the steps of the Minijson script but yet this error still comes up. Some help would be appreciated.
void Start () {
//creating url
image1Request = new WWW("http://development.someurl.com/MoreGames/MoreGames.json");
StartCoroutine(ImageOne(image1Request));
}
IEnumerator ImageOne(WWW www)
{
//wait until url is loaded
yield return www;
//load image into texture slot
if (www.error == null)
{
//assigning URLS
var dict = Json.Deserialize(www.text) as Dictionary<string,object>;
Debug.Log(www.text);
Debug.Log("deserialized: " + dict.GetType());
Debug.Log("dict['string']: " + (string)dict["widget"]);
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
Instead of using
var dict = Json.Deserialize(www.text) as Dictionary<string,object>;
I would suggest you to use the following code snippet
var dict = new System.Web.Script.Serialization.JavaScriptSerializer(); Dictionary<string, object> dictObject =(Dictionary<string,object>)jsSerializer.DeserializeObject(www.text);
Your Dictionary's value type is object. You are trying to cast an object type to string. It should be corrected to :
(object)dict["widget"]
I'm not sure what you are trying to log there, but if it is the widget object's name it should go like this :
Debug.Log("dict['string']: " + ((object)dict["widget"]).ToString());
How can I use JSON.Net and loop through the following JSON to output one HTML image tag (a string) for each member of the "photos" object?
My goal is to read the below JSON and output this string:
"<img src='/images/foo.jpg' alt='Hello World!'><img src='/images/bar.jpg' alt='Another Photo' />"
JSON is stored in external file "photos.json"
{
"photos": {
"photo1": {
"src": "/images/foo.jpg",
"alt": "Hello World!"
},
"photo2": {
"src": "/images/bar.jpg",
"alt": "Another Photo"
}
}
}
I've started with code similar to what's shown here: http://www.hanselman.com/blog/NuGetPackageOfTheWeek4DeserializingJSONWithJsonNET.aspx
var client = new WebClient();
client.Headers.Add("User-Agent", "Nobody");
var response = client.DownloadString(new Uri("http://www.example.com/photos.json"));
JObject o = JObject.Parse(response);'
//Now o is an object I can walk around...
But, I haven't found a way to "walk around o" as shown in the example.
I want to loop through each member of the photos object, read the properties and add html to my string for each photo.
So far, I've tried the examples shown here: http://james.newtonking.com/json/help/index.html?topic=html/QueryJson.htm
But, I cannot make them work once inside a for each loop.
Here is how you can "walk around" your JObject to extract the information you need.
string json = #"
{
""photos"": {
""photo1"": {
""src"": ""/images/foo.jpg"",
""alt"": ""Hello World!""
},
""photo2"": {
""src"": ""/images/bar.jpg"",
""alt"": ""Another Photo""
}
}
}";
StringBuilder sb = new StringBuilder();
JObject o = JObject.Parse(json);
foreach (JProperty prop in o["photos"].Children<JProperty>())
{
JObject photo = (JObject)prop.Value;
sb.AppendFormat("<img src='{0}' alt='{1}' />\r\n",
photo["src"], photo["alt"]);
}
Console.WriteLine(sb.ToString());
Output:
<img src='/images/foo.jpg' alt='Hello World!' />
<img src='/images/bar.jpg' alt='Another Photo' />
First you need to define a class that holds your data and also is able to output itself as an HTML tag:
public class Photo
{
public string Src { get; set; }
public string Alt { get; set; }
public string ToHtml()
{
return string.Format(
"<img src='{0}' alt='{1}'/>,
this.Src,
this.Alt);
}
}
In order to be able to use JSON.Net for creating typed objects, you need to 'normalize' your JSON - it is not entirely in the usual format that would indicate an array of identical objects. You have to entirely get rid of the identifiers photo*1*, photo*2*,.., photo*n*, or you have to make them identical (i.e. they all should simply be photo, without number). If you can control JSON creation, you can do it right there. Otherwise you must manipulate the web response accordingly (e.g. with string.Replace(...)).
Having done that, you can use JSON.Net to get a typed list, and subsequently you can simply iterate through it to get the required HTML:
var client = new WebClient();
client.Headers.Add("User-Agent", "Nobody");
string response = client.DownloadString(new Uri("http://www.example.com/photos.json"));
// --> 'Normalize' response string here, if necessary
List<Photo> photos = JsonConvert.DeserializeObject<List<Photo>>(response);
// now buid the HTML string
var sb = new StringBuilder();
foreach(photo in photos)
{
sb.Append(photo.ToHtml());
}
string fullHtml = sb.ToString();
...