How to use JSON.Net to read JSON and output HTML? - c#

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();
...

Related

Adaptive cards - serving images in bytes

I'm trying to put an image in Adaptive Card in Bot framework like this way:
card.Body.Add(new AdaptiveImage()
{
Type = "Image",
Url = new Uri(pictureUrl),
Size = AdaptiveImageSize.Large
});
It's working. The problem is with Url. I get images from the external web service in Base64 format. But sometimes I get too large image so I get The uri string is too long exception.
Is there any way how to handle that problem? For example, enable putting the image in Adaptive card in bytes.
thanks for reporting this issue. The root cause is that the pictureUrl is longer than .NET's max Uri length. We're tracking fixing this here.
There's a pretty simple workaround available, since the limitation is occurring in the .NET C# library which you're using to simply author the card, but WebChat doesn't use the C# library to display cards (it uses the JS library, and JS/HTML doesn't have a length limit!). Therefore, the only thing that isn't working in your case is generating the JSON... but there's a simple fix!
Workaround:
Define the following class, extending AdaptiveImage, adding a LongUrl property (which writes to the same url property in the JSON).
public class AdaptiveImageWithLongUrl : AdaptiveImage
{
[JsonProperty(PropertyName = "url", Required = Required.Always)]
public string LongUrl { get; set; }
}
Then, use your new image class and the new property when assigning long urls!
// A data URL that's longer than .NET max length
string actualUrl = "data:image/gif;base64," + string.Join("", new int[120000].Select(i => "A")) + "end";
AdaptiveCard card = new AdaptiveCard("1.0")
{
Body =
{
new AdaptiveImageWithLongUrl()
{
LongUrl = actualUrl
}
}
};
// Place the JObject in the attachment!
var attachment = new Attachment()
{
Content = card,
ContentType = "application/vnd.microsoft.card.adaptive",
Name = "cardName"
};

Fiddler access nested json to modify

i somehow can´t reach the objects in a nested json. I easily can reach the element test, but how to reach this_month inside "stats"? this is my json:
{"test":"OK","stats":{"this_month":"1653","this_week":"1653"}}
this is my code inside the customrules.cs
var oResponseBody = oSession.GetResponseBodyAsString();
JSON.JSONParseResult oJSON = JSON.JsonDecode(oResponseBody) as JSON.JSONParseResult;
Hashtable oObj = oJSON.JSONObject as Hashtable;
oObj["test"] = "changed";
var modBytes = Fiddler.WebFormats.JSON.JsonEncode(oObj);
// Convert json to bytes, storing the bytes in request body
var mod = System.Text.Encoding.UTF8.GetBytes(modBytes);
oSession.ResponseBody = mod;
As soon as I want to acces this_month in "stats" with this: oObj["stats"]["this_month"] = "changed"; my fiddler get´s errors and crashes.

Elasticsearch - MapperParsingException[Malformed content, must start with an object]

I am trying to bulk index document into ES using BulkDescriptor in C#. i am using V1.7 ES. Following is my piece of code,
public IBulkResponse IndexBulk(string index, string type, List<string> documents)
{
BulkDescriptor descriptor = new BulkDescriptor();
foreach (var doc in documents)
{
JObject data = JObject.Parse(documents);
descriptor.Index<object>(i => i
.Index(index)
.Type(type)
.Id(data["Id"].toString())
.Document(doc));
}
return _Client.Bulk(descriptor);
}
But it is not inserting the documents, When i verified the response i saw the following message MapperParsingException[Malformed content, must start with an object]
Sample JSON document
{
"a" : "abc",
"b": { "c": ["1","2"]}
}
What went wrong in it?
Issue here is passing raw json through strongly typed fluent bulk method.
What you are actually sending to elasticsearch is
{"index":{"_index":"test1","_type":"string"}}
"{"a" : "abc","b": { "c": ["1","2"]}}"
which is not correct.
Few ideas what you can do about this:
use JObject to send correctly serialized object to elasticsearch
descriptor.Index<JObject>(i => i
.Index(index)
.Type(type)
.Id(data["Id"].toString())
.Document(JObject.Parse(doc)));
take advantage of using .Raw client to send raw json
var json = new StringBuilder();
json.AppendLine(#"{""index"":{""_index"":""indexName"",""_type"":""typeName""}}");
json.AppendLine(#"{""a"" : ""abc"",""b"": { ""c"": [""1"",""2""]}}");
_Client.Raw.Bulk(json2.ToString());
Hope it helps.

C# Extracting data from Json or DataSets - Porting from Python (Json to Dict)

I have the following Python script which I need to port to C#. This gets a JSON response from a URL and then pops it into a dictionary. Then it checks for the data next_page and if there is data (it's not empty) it then returns true. Underneath I'll paste the C# code I have but I'm really struggling to do the final part. I don't know and I certainly don't want to understand the data in the JSON response, I just want to know if the field next_page is there.
# Gets JSON response
response = requests.get(url, auth=(user, pwd))
if response.status_code != 200:
print('Status:', response.status_code, 'Problem with the request. Exiting.')
exit()
data = response.json()
if(data['next_page']):
return True
else:
return False
So this is the c# code I've got:
using Newtonsoft.Json;
string response = "";
using (WebClient client = new WebClient())
{
client.UseDefaultCredentials = true;
client.Credentials = new NetworkCredential(user, password);
try
{
response = client.DownloadString(url);
} catch (Exception e)
{
throw e;
}
}
XmlDocument xml = JsonConvert.DeserializeXmlNode(json, "RootObject");
XmlReader xr = new XmlNodeReader(xml);
DataSet ds = new DataSet("Json Data");
ds.ReadXml(xr);
From what I've seen on the web DataSets work best when you know what the data inside of it is. I just want to know if there is a field called next_page and if there is, is it empty or does it have data. I'm just struggling to get anything out of the DataSet.
You will want to include the JSON.net nuget package (http://james.newtonking.com/json) this lets you deserialize the JSON response into a dictionary (or preferably a new class) allowing you to access the response.
eg add this into your try catch after including the library
var dict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
Alternativly you could create a new class that represents the expected JSON and deserialize into that
public class ResponseObject
{
public string next_page { get; set; }
}
var responseResult = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseObject>(response);

Calling Json script and displaying the Images within the script

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);

Categories