I have this response from server
[{
"sys_id": "******************************",
"dv_model_id": "*****************",
"due": "YYYY-MM-DD HH:mm:ss",
"assigned_to": "1524s32a54dss412s121s",
"dv_assigned_to": "username",
"assigned_to.phone": "+12345678910",
"assigned_to.email": "abc#a.c",
"u_borrower_id": "fb36e45f0a12452004742183457e833b0",
"dv_u_borrower_id": "antoherUserName",
"u_borrower_id.phone": "+12345678910",
"u_borrower_id.email": "abcf#a.c"
}
,{....}
,{....}]
I'm trying to deserialize this to List
public class Inventory
{
public Inventory()
{
assigned_to = new User();
u_borrower_wwid = new User();
}
public string sys_ID { get; set; }
public string dv_model_id { get; set; }
public DateTime due { get; set; }
public string dv_assigned_to { get; set; }
public User assigned_to { get; set; }
public string dv_u_borrower_id { get; set; }
public User u_borrower_id { get; set; }
}
now, since the JSON contains - "assigned_to": "1524s32a54dss412s121s","
the deserialization failed.
the same with the - ""u_borrower_id": "fb36e45f0a12452004742183457e833b0"," .
do you know any way to ignore them? or remove them from the JSON?
I need only the properties (".phone" and ".email") of the object.
any ideas?
I see a number of solutions:
Modify your Inventory object (or create a new one) so the json can be fully deserialized, then access the values there.
Your new and updated object should look like this:
public class InventoryJsonObject
{
public string sys_id { get; set; }
public string dv_model_id { get; set; }
public string due { get; set; }
public string assigned_to { get; set; }
public string dv_assigned_to { get; set; }
[JsonProperty("assigned_to.phone")]
public string assigned_to_phone { get; set; }
[JsonProperty("assigned_to.email")]
public string assigned_to_email { get; set; }
public string u_borrower_id { get; set; }
public string dv_u_borrower_id { get; set; }
[JsonProperty("u_borrower_id.phone")]
public string u_borrower_id_phone { get; set; }
[JsonProperty("u_borrower_id.email")]
public string u_borrower_id_email { get; set; }
}
Use regular expressions to get the values from the string. In this case your regex would be "u_borrower_id\.phone": "(.*?)" and "u_borrower_id\.email": "(.*?)"
The complete regex solution could look like this (assuming every object has a phone and email included):
string phonePattern = "\"u_borrower_id\\.phone\": \"(.*?)\"";
string emailPattern = "\"u_borrower_id\\.email\": \"(.*?)\"";
Regex phoneRegex = new Regex(phonePattern);
var phoneMatches = phoneRegex.Matches(input);
Regex emailRegex = new Regex(emailPattern);
var emailMatches = emailRegex.Matches(input);
for (int i = 0; i < phoneMatches.Count; i++)
{
string phoneMatch = phoneMatches[i].Groups[1].Value;
string emailMatch = emailMatches[i].Groups[1].Value;
// Now you can add them to any collection you desire
}
Implement a cast between string and User. Since your error originates from the fact that the string fb36e45f0a12452004742183457e833b0 cannot be cast into a User object trivially, you have to implement the cast. It would look like this:
public static implicit operator User(string _string)
{
// This could be a DB lookup, or basically anything else
return new User()
{
id = _string
};
}
In order to avoid unnecessary casting that causes bug you can use this workaround by creating a Dictionary<string, object>
and read only the properties you need as strings and convert them to your desired type:
using System.Web.Script;
Dictionary<string, object> dict = Serialization.JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
Now you can modify your class properties one by one like:
(You can create additional method for your DateTime and User property)
Inventory inventory = new Inventory();
//notice i'v added the 'u_borrower_id_email' property to your class:
inventory.u_borrower_id_email = dict.GetStringOrDefault("u_borrower_id.phone");
private static string GetStringOrDefault(this Dictionary<string, object> data, string key)
{
string result = "";
object o;
if (data.TryGetValue(key, out o))
{
if (o != null)
{
result = o.ToString();
}
}
return result;
}
I want to parse tree but it seems many nodes have the same name and attribute as well, so trying to parse it in a efficient way so it can be reused via classes.
<TestStructure>
<TestStructureElement Id="ID_TSE_6" Type="FunctionHierarchy" ObjectRef="ID_FuncHierarchy_7" T42ElementId="31391123">
<TestStructureElement Id="ID_TSE_12" Type="Function" ObjectRef="ID_Function_13" T42ElementId="31391126">
<TestStructureElement Id="ID_TSE_14" Type="Function" ObjectRef="ID_Function_15" T42ElementId="31391127">
<TestStructureElement Id="ID_TSE_16" Type="Function" ObjectRef="ID_Function_17" T42ElementId="31391128"/>
</TestStructureElement>
<TestStructureElement Id="ID_TSE_18" Type="Function" ObjectRef="ID_Function_19" T42ElementId="31391129">
<TestStructureElement Id="ID_TSE_20" Type="Function" ObjectRef="ID_Function_21" T42ElementId="31391130"/>
</TestStructureElement>
<TestStructureElement Id="ID_TSE_26" Type="TestCase" ObjectRef="ID_TestCase_27" T42ElementId="31391133" DOORSId="ESP-TC-104">
<TestResults TestState="EXECUTED">
<Evaluations>
<Evaluation VDSEvaluation="VDS_8">
<Remark>Hochreibwert =1 nicht gegeben</Remark>
</Evaluation>
</Evaluations>
<TestCaseActivities>
<TestCaseActivity TCActivity="TESTCASE" TCActivityState="NONE"/>
<TestCaseActivity TCActivity="VDS" TCActivityState="NONE"/>
<TestCaseActivity TCActivity="SETUP" TCActivityState="NONE"/>
<TestCaseActivity TCActivity="RESUBMISSION" TCActivityState="NONE"/>
</TestCaseActivities>
</TestResults>
</TestStructureElement>
</TestStructure>
Try xml linq. I usd a recursive algorithm
Code below assumes only one Test Report and makes it static
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
new TestStructureElement(FILENAME);
}
}
public class TestStructureElement
{
public static TestStructureElement root = new TestStructureElement();
public string id { get; set; }
public string _Type { get; set;}
public string objectRef { get; set; }
public int t42ElementId { get;set;}
public List<TestStructureElement> children { get;set;}
public static TestResults testResults { get; set; }
public TestStructureElement() { }
public TestStructureElement(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement testStructure = doc.Descendants("TestStructureElement").FirstOrDefault();
GetTree(testStructure, root);
}
public void GetTree(XElement xml, TestStructureElement testStructure)
{
testStructure.id = (string)xml.Attribute("Id");
testStructure._Type = (string)xml.Attribute("Type");
testStructure.objectRef = (string)xml.Attribute("ObjectRef");
testStructure.t42ElementId = (int)xml.Attribute("T42ElementId");
if (xml.Elements("TestStructureElement") != null)
{
testStructure.children = new List<TestStructureElement>();
foreach (XElement child in xml.Elements("TestStructureElement"))
{
TestStructureElement childStructure = new TestStructureElement();
testStructure.children.Add(childStructure);
GetTree(child, childStructure);
}
}
TestStructureElement.testResults = xml.Descendants("TestResults").Select(x => new TestResults()
{
testState = (string)x.Attribute("TestState"),
evalaution = (string)x.Descendants("Evaluation").FirstOrDefault().Attribute("VDSEvaluation"),
evaluationRemark = (string)x.Descendants("Evaluation").FirstOrDefault().Element("Remark"),
testCaseActivity = x.Descendants("TestCaseActivity")
.GroupBy(y => (string)y.Attribute("TCActivity"), z => (string)z.Attribute("TCActivityState"))
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
public class TestResults
{
public string testState { get; set; }
public string evalaution { get; set; }
public string evaluationRemark { get; set; }
public Dictionary<string, string> testCaseActivity { get; set; }
}
}
Code below has one change which doesn't make Test Report Static
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
new TestStructureElement(FILENAME);
}
}
public class TestStructureElement
{
public static TestStructureElement root = new TestStructureElement();
public string id { get; set; }
public string _Type { get; set;}
public string objectRef { get; set; }
public int t42ElementId { get;set;}
public List<TestStructureElement> children { get;set;}
public TestResults testResults { get; set; }
public TestStructureElement() { }
public TestStructureElement(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement testStructure = doc.Descendants("TestStructureElement").FirstOrDefault();
GetTree(testStructure, root);
}
public void GetTree(XElement xml, TestStructureElement testStructure)
{
testStructure.id = (string)xml.Attribute("Id");
testStructure._Type = (string)xml.Attribute("Type");
testStructure.objectRef = (string)xml.Attribute("ObjectRef");
testStructure.t42ElementId = (int)xml.Attribute("T42ElementId");
if (xml.Elements("TestStructureElement") != null)
{
testStructure.children = new List<TestStructureElement>();
foreach (XElement child in xml.Elements("TestStructureElement"))
{
TestStructureElement childStructure = new TestStructureElement();
testStructure.children.Add(childStructure);
GetTree(child, childStructure);
}
}
testStructure.testResults = xml.Descendants("TestResults").Select(x => new TestResults()
{
testState = (string)x.Attribute("TestState"),
evalaution = (string)x.Descendants("Evaluation").FirstOrDefault().Attribute("VDSEvaluation"),
evaluationRemark = (string)x.Descendants("Evaluation").FirstOrDefault().Element("Remark"),
testCaseActivity = x.Descendants("TestCaseActivity")
.GroupBy(y => (string)y.Attribute("TCActivity"), z => (string)z.Attribute("TCActivityState"))
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
public class TestResults
{
public string testState { get; set; }
public string evalaution { get; set; }
public string evaluationRemark { get; set; }
public Dictionary<string, string> testCaseActivity { get; set; }
}
}
I have this class which represent a node TestCase in my XML :
public class TestCase
{
[XmlAttribute("name")]
public string name { get; set; }
public string version { get; set; }
public string verdict { get; set; }
public string objective { get; set; }
public string criteria { get; set; }
public string issue { get; set; }
public string clientcomments { get; set; }
public string authoritycomments { get; set; }
public string sdk { get; set; }
}
I use XmlNode.SelectSingleNode to fetch a specific node in my XML. For info, there are no duplicate nodes (no nodes with the same name attribute) if it matters.
So far, I have this code :
public static TestCase FetchNode(string NodeName, string Path)
{
TestCase testcase = new TestCase();
string[] attr = { "name", "version", "verdict", "objective", "criteria"
, "issue", "clientcomments", "authoritycomments", "sdk" };
string[] attrval = { null, null,null,null,null,null,null,null,null};
XmlDocument doc = new XmlDocument();
doc.Load(Path);
XmlNode node = doc.SelectSingleNode("/TestsList/TestCase[#name='" + NodeName + "']");
for (var i = 0; i == attr.Length - 1;i++)
{
attrval[i] = node[attr[i]].InnerText;
}
testcase.name = attrval[0];
testcase.version = attrval[1];
testcase.verdict = attrval[2];
testcase.objective = attrval[3];
testcase.criteria = attrval[4];
testcase.issue = attrval[5];
testcase.clientcomments = attrval[6];
testcase.authoritycomments = attrval[7];
testcase.sdk = attrval[8];
return testcase;
}
However, this code is not scalable at all, if I change my class structure, I would need to change the function because each element of the class are hardcoded in it.
This is a wide request, but how could I write this function so if I add or remove a string in the class definition of TestCase, I don`t have to change the function FetchNode.
Thank you for your time.
You could use XmlSerializer.Deserialize
Example:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class TestCase
{
[XmlAttribute("name")]
public string name { get; set; }
public string version { get; set; }
public string verdict { get; set; }
public string objective { get; set; }
public string criteria { get; set; }
public string issue { get; set; }
public string clientcomments { get; set; }
public string authoritycomments { get; set; }
public string sdk { get; set; }
}
public class Program
{
public const string XML = #"
<TestCase name='TicketName'>
<name>Jon Nameson</name>
<version>10.1</version>
<verdict>High</verdict>
</TestCase>
";
public static void Main()
{
var doc = new XmlDocument();
doc.LoadXml(XML);
var node = doc.SelectSingleNode("/TestCase");
var serializer = new XmlSerializer(typeof(TestCase));
var testcase = serializer.Deserialize(new StringReader(node.OuterXml)) as TestCase;
Console.WriteLine(testcase.name);
Console.WriteLine(testcase.version);
Console.WriteLine(testcase.verdict);
}
}
DotNetFiddle
You can deserialize directly from your selected XmlNode by combining XmlSerializer with XmlNodeReader using the following extension method:
public static class XmlNodeExtensions
{
public static T Deserialize<T>(this XmlNode element, XmlSerializer serializer = null)
{
using (var reader = new ProperXmlNodeReader(element))
return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
}
class ProperXmlNodeReader : XmlNodeReader
{
// Bug fix from https://stackoverflow.com/questions/30102275/deserialize-object-property-with-stringreader-vs-xmlnodereader
public ProperXmlNodeReader(XmlNode node)
: base(node)
{
}
public override string LookupNamespace(string prefix)
{
return NameTable.Add(base.LookupNamespace(prefix));
}
}
}
This adds an extension method to XmlNode which invokes XmlSerializer to deserialize the selected node to an instance of the generic type T.
Then do:
var testcase = node.Deserialize<TestCase>();
which is identical to:
var testcase = XmlNodeExtensions.Deserialize<TestCase>(node);
In your case the expected root element name of your TestCase class, namely <TestCase>, matches the actual node name. If the node name does not match the expected root element name, you can tell XmlSerializer to expect a different root name by following the instructions in XmlSerializer Performance Issue when Specifying XmlRootAttribute.
I'm trying to parse some JSON data from the Google AJAX Search API. I have this URL and I'd like to break it down so that the results are displayed. I've currently written this code, but I'm pretty lost in regards of what to do next, although there are a number of examples out there with simplified JSON strings.
Being new to C# and .NET in general I've struggled to get a genuine text output for my ASP.NET page so I've been recommended to give JSON.NET a try. Could anyone point me in the right direction to just simply writing some code that'll take in JSON from the Google AJAX Search API and print it out to the screen?
EDIT: ALL FIXED! All results are working fine. Thank you again Dreas Grech!
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.ServiceModel.Web;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Text;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
GoogleSearchResults g1 = new GoogleSearchResults();
const string json = #"{""responseData"": {""results"":[{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://www.cheese.com/"",""url"":""http://www.cheese.com/"",""visibleUrl"":""www.cheese.com"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:bkg1gwNt8u4J:www.cheese.com"",""title"":""\u003cb\u003eCHEESE\u003c/b\u003e.COM - All about \u003cb\u003echeese\u003c/b\u003e!."",""titleNoFormatting"":""CHEESE.COM - All about cheese!."",""content"":""\u003cb\u003eCheese\u003c/b\u003e - everything you want to know about it. Search \u003cb\u003echeese\u003c/b\u003e by name, by types of milk, by textures and by countries.""},{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://en.wikipedia.org/wiki/Cheese"",""url"":""http://en.wikipedia.org/wiki/Cheese"",""visibleUrl"":""en.wikipedia.org"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:n9icdgMlCXIJ:en.wikipedia.org"",""title"":""\u003cb\u003eCheese\u003c/b\u003e - Wikipedia, the free encyclopedia"",""titleNoFormatting"":""Cheese - Wikipedia, the free encyclopedia"",""content"":""\u003cb\u003eCheese\u003c/b\u003e is a food consisting of proteins and fat from milk, usually the milk of cows, buffalo, goats, or sheep. It is produced by coagulation of the milk \u003cb\u003e...\u003c/b\u003e""},{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://www.ilovecheese.com/"",""url"":""http://www.ilovecheese.com/"",""visibleUrl"":""www.ilovecheese.com"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:GBhRR8ytMhQJ:www.ilovecheese.com"",""title"":""I Love \u003cb\u003eCheese\u003c/b\u003e!, Homepage"",""titleNoFormatting"":""I Love Cheese!, Homepage"",""content"":""The American Dairy Association\u0026#39;s official site includes recipes and information on nutrition and storage of \u003cb\u003echeese\u003c/b\u003e.""},{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://www.gnome.org/projects/cheese/"",""url"":""http://www.gnome.org/projects/cheese/"",""visibleUrl"":""www.gnome.org"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:jvfWnVcSFeQJ:www.gnome.org"",""title"":""\u003cb\u003eCheese\u003c/b\u003e"",""titleNoFormatting"":""Cheese"",""content"":""\u003cb\u003eCheese\u003c/b\u003e uses your webcam to take photos and videos, applies fancy special effects and lets you share the fun with others. It was written as part of Google\u0026#39;s \u003cb\u003e...\u003c/b\u003e""}],""cursor"":{""pages"":[{""start"":""0"",""label"":1},{""start"":""4"",""label"":2},{""start"":""8"",""label"":3},{""start"":""12"",""label"":4},{""start"":""16"",""label"":5},{""start"":""20"",""label"":6},{""start"":""24"",""label"":7},{""start"":""28"",""label"":8}],""estimatedResultCount"":""14400000"",""currentPageIndex"":0,""moreResultsUrl"":""http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den-GB\u0026q\u003dcheese""}}, ""responseDetails"": null, ""responseStatus"": 200}";
g1 = JSONHelper.Deserialise<GoogleSearchResults>(json);
Response.Write(g1.content);
}
}
public class JSONHelper
{
public static T Deserialise<T>(string json)
{
T obj = Activator.CreateInstance<T>();
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
DataContractJsonSerializer serialiser = new DataContractJsonSerializer(obj.GetType());
ms.Close();
return obj;
}
}
/// Deserialise from JSON
[Serializable]
public class GoogleSearchResults
{
public GoogleSearchResults() { }
public GoogleSearchResults(string _unescapedUrl, string _url, string _visibleUrl, string _cacheUrl, string _title, string _titleNoFormatting, string _content)
{
this.unescapedUrl = _unescapedUrl;
this.url = _url;
this.visibleUrl = _visibleUrl;
this.cacheUrl = _cacheUrl;
this.title = _title;
this.titleNoFormatting = _titleNoFormatting;
this.content = _content;
}
string _unescapedUrl;
string _url;
string _visibleUrl;
string _cacheUrl;
string _title;
string _titleNoFormatting;
string _content;
[DataMember]
public string unescapedUrl
{
get { return _unescapedUrl; }
set { _unescapedUrl = value; }
}
[DataMember]
public string url
{
get { return _url; }
set { _url = value; }
}
[DataMember]
public string visibleUrl
{
get { return _visibleUrl; }
set { _visibleUrl = value; }
}
[DataMember]
public string cacheUrl
{
get { return _cacheUrl; }
set { _cacheUrl = value; }
}
[DataMember]
public string title
{
get { return _title; }
set { _title = value; }
}
[DataMember]
public string titleNoFormatting
{
get { return _titleNoFormatting; }
set { _titleNoFormatting = value; }
}
[DataMember]
public string content
{
get { return _content; }
set { _content = value; }
}
}
The code currently compiles and runs perfectly, but isn't returning any results. Could someone help me with returning what I require, the results ready to print out to the screen?
Edit:
Json.NET works using the same JSON and classes as the example above.
GoogleSearchResults g1 = JsonConvert.DeserializeObject<GoogleSearchResults>(json);
Link: Serializing and Deserializing JSON with Json.NET
Related
C# - parsing json formatted data into nested hashtables
Parse JSON array
[Update]
I've just realized why you weren't receiving results back... you have a missing line in your Deserialize method. You were forgetting to assign the results to your obj :
public static T Deserialize<T>(string json)
{
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
Also, just for reference, here is the Serialize method :
public static string Serialize<T>(T obj)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
Edit
If you want to use Json.NET here are the equivalent Serialize/Deserialize methods to the code above..
Deserialize:
JsonConvert.DeserializeObject<T>(string json);
Serialize:
JsonConvert.SerializeObject(object o);
This are already part of Json.NET so you can just call them on the JsonConvert class.
Link: Serializing and Deserializing JSON with Json.NET
Now, the reason you're getting a StackOverflow is because of your Properties.
Take for example this one :
[DataMember]
public string unescapedUrl
{
get { return unescapedUrl; } // <= this line is causing a Stack Overflow
set { this.unescapedUrl = value; }
}
Notice that in the getter, you are returning the actual property (ie the property's getter is calling itself over and over again), and thus you are creating an infinite recursion.
Properties (in 2.0) should be defined like such :
string _unescapedUrl; // <= private field
[DataMember]
public string unescapedUrl
{
get { return _unescapedUrl; }
set { _unescapedUrl = value; }
}
You have a private field and then you return the value of that field in the getter, and set the value of that field in the setter.
Btw, if you're using the 3.5 Framework, you can just do this and avoid the backing fields, and let the compiler take care of that :
public string unescapedUrl { get; set;}
I found this approach which parse JSON into a dynamic object, it extends a DynamicObject and JavascriptConverter to turn the string into an object.
DynamicJsonObject
public class DynamicJsonObject : DynamicObject
{
private IDictionary<string, object> Dictionary { get; set; }
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
this.Dictionary = dictionary;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this.Dictionary[binder.Name];
if (result is IDictionary<string, object>)
{
result = new DynamicJsonObject(result as IDictionary<string, object>);
}
else if (result is ArrayList && (result as ArrayList) is IDictionary<string, object>)
{
result = new List<DynamicJsonObject>((result as ArrayList).ToArray().Select(x => new DynamicJsonObject(x as IDictionary<string, object>)));
}
else if (result is ArrayList)
{
result = new List<object>((result as ArrayList).ToArray());
}
return this.Dictionary.ContainsKey(binder.Name);
}
}
Converter
public class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
if (type == typeof(object))
{
return new DynamicJsonObject(dictionary);
}
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(object) })); }
}
}
Usage (sample json):
JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;
Console.WriteLine("glossaryEntry.glossary.title: " + glossaryEntry.glossary.title);
Console.WriteLine("glossaryEntry.glossary.GlossDiv.title: " + glossaryEntry.glossary.GlossDiv.title);
Console.WriteLine("glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.ID: " + glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.ID);
Console.WriteLine("glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.para: " + glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.para);
foreach (var also in glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso)
{
Console.WriteLine("glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso: " + also);
}
This method has to return true, otherwise it will throw an error. E.g. you can throw an error if a key does not exist.
Returning true and emptying result will return an empty value rather than throwing an error.
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!this.Dictionary.ContainsKey(binder.Name))
{
result = "";
}
else
{
result = this.Dictionary[binder.Name];
}
if (result is IDictionary<string, object>)
{
result = new DynamicJsonObject(result as IDictionary<string, object>);
}
else if (result is ArrayList && (result as ArrayList) is IDictionary<string, object>)
{
result = new List<DynamicJsonObject>((result as ArrayList).ToArray().Select(x => new DynamicJsonObject(x as IDictionary<string, object>)));
}
else if (result is ArrayList)
{
result = new List<object>((result as ArrayList).ToArray());
}
return true; // this.Dictionary.ContainsKey(binder.Name);
}
Your data class doesn't match the JSON object. Use this instead:
[DataContract]
public class GoogleSearchResults
{
[DataMember]
public ResponseData responseData { get; set; }
}
[DataContract]
public class ResponseData
{
[DataMember]
public IEnumerable<Results> results { get; set; }
}
[DataContract]
public class Results
{
[DataMember]
public string unescapedUrl { get; set; }
[DataMember]
public string url { get; set; }
[DataMember]
public string visibleUrl { get; set; }
[DataMember]
public string cacheUrl { get; set; }
[DataMember]
public string title { get; set; }
[DataMember]
public string titleNoFormatting { get; set; }
[DataMember]
public string content { get; set; }
}
Also, you don't have to instantiate the class to get its type for deserialization:
public static T Deserialise<T>(string json)
{
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
var serialiser = new DataContractJsonSerializer(typeof(T));
return (T)serialiser.ReadObject(ms);
}
}
I just think the whole example would be useful. This is the example for this problem.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.ServiceModel.Web;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Text;
using System.Collections.Generic;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
GoogleSearchResults g1 = new GoogleSearchResults();
const string json = #"{""responseData"": {""results"":[{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://www.cheese.com/"",""url"":""http://www.cheese.com/"",""visibleUrl"":""www.cheese.com"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:bkg1gwNt8u4J:www.cheese.com"",""title"":""\u003cb\u003eCHEESE\u003c/b\u003e.COM - All about \u003cb\u003echeese\u003c/b\u003e!."",""titleNoFormatting"":""CHEESE.COM - All about cheese!."",""content"":""\u003cb\u003eCheese\u003c/b\u003e - everything you want to know about it. Search \u003cb\u003echeese\u003c/b\u003e by name, by types of milk, by textures and by countries.""},{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://en.wikipedia.org/wiki/Cheese"",""url"":""http://en.wikipedia.org/wiki/Cheese"",""visibleUrl"":""en.wikipedia.org"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:n9icdgMlCXIJ:en.wikipedia.org"",""title"":""\u003cb\u003eCheese\u003c/b\u003e - Wikipedia, the free encyclopedia"",""titleNoFormatting"":""Cheese - Wikipedia, the free encyclopedia"",""content"":""\u003cb\u003eCheese\u003c/b\u003e is a food consisting of proteins and fat from milk, usually the milk of cows, buffalo, goats, or sheep. It is produced by coagulation of the milk \u003cb\u003e...\u003c/b\u003e""},{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://www.ilovecheese.com/"",""url"":""http://www.ilovecheese.com/"",""visibleUrl"":""www.ilovecheese.com"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:GBhRR8ytMhQJ:www.ilovecheese.com"",""title"":""I Love \u003cb\u003eCheese\u003c/b\u003e!, Homepage"",""titleNoFormatting"":""I Love Cheese!, Homepage"",""content"":""The American Dairy Association\u0026#39;s official site includes recipes and information on nutrition and storage of \u003cb\u003echeese\u003c/b\u003e.""},{""GsearchResultClass"":""GwebSearch"",""unescapedUrl"":""http://www.gnome.org/projects/cheese/"",""url"":""http://www.gnome.org/projects/cheese/"",""visibleUrl"":""www.gnome.org"",""cacheUrl"":""http://www.google.com/search?q\u003dcache:jvfWnVcSFeQJ:www.gnome.org"",""title"":""\u003cb\u003eCheese\u003c/b\u003e"",""titleNoFormatting"":""Cheese"",""content"":""\u003cb\u003eCheese\u003c/b\u003e uses your webcam to take photos and videos, applies fancy special effects and lets you share the fun with others. It was written as part of Google\u0026#39;s \u003cb\u003e...\u003c/b\u003e""}],""cursor"":{""pages"":[{""start"":""0"",""label"":1},{""start"":""4"",""label"":2},{""start"":""8"",""label"":3},{""start"":""12"",""label"":4},{""start"":""16"",""label"":5},{""start"":""20"",""label"":6},{""start"":""24"",""label"":7},{""start"":""28"",""label"":8}],""estimatedResultCount"":""14400000"",""currentPageIndex"":0,""moreResultsUrl"":""http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den-GB\u0026q\u003dcheese""}}, ""responseDetails"": null, ""responseStatus"": 200}";
g1 = JSONHelper.Deserialise<GoogleSearchResults>(json);
foreach (Pages x in g1.responseData.cursor.pages)
{
// Anything you want to get
Response.Write(x.label);
}
}
}
public class JSONHelper
{
public static T Deserialise<T>(string json)
{
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
var serialiser = new DataContractJsonSerializer(typeof(T));
return (T)serialiser.ReadObject(ms);
}
}
public static string Serialize<T>(T obj)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
}
[DataContract]
public class GoogleSearchResults
{
[DataMember]
public ResponseData responseData { get; set; }
[DataMember]
public string responseStatus { get; set; }
}
public class ResponseData
{
[DataMember]
public Cursor cursor { get; set; }
[DataMember]
public IEnumerable<Results> results { get; set; }
}
[DataContract]
public class Cursor
{
[DataMember]
public IEnumerable<Pages> pages { get; set; }
}
[DataContract]
public class Pages
{
[DataMember]
public string start { get; set; }
[DataMember]
public string label { get; set; }
}
[DataContract]
public class Results
{
[DataMember]
public string unescapedUrl { get; set; }
[DataMember]
public string url { get; set; }
[DataMember]
public string visibleUrl { get; set; }
[DataMember]
public string cacheUrl { get; set; }
[DataMember]
public string title { get; set; }
[DataMember]
public string titleNoFormatting { get; set; }
[DataMember]
public string content { get; set; }
}
I tried to use the code above but didn't work. The JSON structure returned by Google is so different and there is a very important miss in the helper function: a call to DataContractJsonSerializer.ReadObject() that actually deserializes the JSON data into the object.
Here is the code that WORKS in 2011:
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Text;
using System.Collections.Generic;
namespace <YOUR_NAMESPACE>
{
public class JSONHelper
{
public static T Deserialise<T>(string json)
{
T obj = Activator.CreateInstance<T>();
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
DataContractJsonSerializer serialiser = new DataContractJsonSerializer(obj.GetType());
obj = (T)serialiser.ReadObject(ms);
ms.Close();
return obj;
}
}
public class Result
{
public string GsearchResultClass { get; set; }
public string unescapedUrl { get; set; }
public string url { get; set; }
public string visibleUrl { get; set; }
public string cacheUrl { get; set; }
public string title { get; set; }
public string titleNoFormatting { get; set; }
public string content { get; set; }
}
public class Page
{
public string start { get; set; }
public int label { get; set; }
}
public class Cursor
{
public string resultCount { get; set; }
public Page[] pages { get; set; }
public string estimatedResultCount { get; set; }
public int currentPageIndex { get; set; }
public string moreResultsUrl { get; set; }
public string searchResultTime { get; set; }
}
public class ResponseData
{
public Result[] results { get; set; }
public Cursor cursor { get; set; }
}
public class GoogleSearchResults
{
public ResponseData responseData { get; set; }
public object responseDetails { get; set; }
public int responseStatus { get; set; }
}
}
To get the content of the first result, do:
GoogleSearchResults googleResults = new GoogleSearchResults();
googleResults = JSONHelper.Deserialise<GoogleSearchResults>(jsonData);
string contentOfFirstResult = googleResults.responseData.results[0].content;
Thank you all for your help.
This is my final version, and it works thanks to your combined help !
I am only showing the changes i made, all the rest is taken from Joe Chung's work
public class GoogleSearchResults
{
[DataMember]
public ResponseData responseData { get; set; }
[DataMember]
public string responseDetails { get; set; }
[DataMember]
public int responseStatus { get; set; }
}
and
[DataContract]
public class ResponseData
{
[DataMember]
public List<Results> results { get; set; }
}
Google Map API request and parse DirectionsResponse with C#, change the json in your url to xml
and use the following code to turn the result into a usable C# Generic List Object.
Took me a while to make. But here it is
var url = String.Format("http://maps.googleapis.com/maps/api/directions/xml?...");
var result = new System.Net.WebClient().DownloadString(url);
var doc = XDocument.Load(new StringReader(result));
var DirectionsResponse = doc.Elements("DirectionsResponse").Select(l => new
{
Status = l.Elements("status").Select(q => q.Value).FirstOrDefault(),
Route = l.Descendants("route").Select(n => new
{
Summary = n.Elements("summary").Select(q => q.Value).FirstOrDefault(),
Leg = n.Elements("leg").ToList().Select(o => new
{
Step = o.Elements("step").Select(p => new
{
Travel_Mode = p.Elements("travel_mode").Select(q => q.Value).FirstOrDefault(),
Start_Location = p.Elements("start_location").Select(q => new
{
Lat = q.Elements("lat").Select(r => r.Value).FirstOrDefault(),
Lng = q.Elements("lng").Select(r => r.Value).FirstOrDefault()
}).FirstOrDefault(),
End_Location = p.Elements("end_location").Select(q => new
{
Lat = q.Elements("lat").Select(r => r.Value).FirstOrDefault(),
Lng = q.Elements("lng").Select(r => r.Value).FirstOrDefault()
}).FirstOrDefault(),
Polyline = p.Elements("polyline").Select(q => new
{
Points = q.Elements("points").Select(r => r.Value).FirstOrDefault()
}).FirstOrDefault(),
Duration = p.Elements("duration").Select(q => new
{
Value = q.Elements("value").Select(r => r.Value).FirstOrDefault(),
Text = q.Elements("text").Select(r => r.Value).FirstOrDefault(),
}).FirstOrDefault(),
Html_Instructions = p.Elements("html_instructions").Select(q => q.Value).FirstOrDefault(),
Distance = p.Elements("distance").Select(q => new
{
Value = q.Elements("value").Select(r => r.Value).FirstOrDefault(),
Text = q.Elements("text").Select(r => r.Value).FirstOrDefault(),
}).FirstOrDefault()
}).ToList(),
Duration = o.Elements("duration").Select(p => new
{
Value = p.Elements("value").Select(q => q.Value).FirstOrDefault(),
Text = p.Elements("text").Select(q => q.Value).FirstOrDefault()
}).FirstOrDefault(),
Distance = o.Elements("distance").Select(p => new
{
Value = p.Elements("value").Select(q => q.Value).FirstOrDefault(),
Text = p.Elements("text").Select(q => q.Value).FirstOrDefault()
}).FirstOrDefault(),
Start_Location = o.Elements("start_location").Select(p => new
{
Lat = p.Elements("lat").Select(q => q.Value).FirstOrDefault(),
Lng = p.Elements("lng").Select(q => q.Value).FirstOrDefault()
}).FirstOrDefault(),
End_Location = o.Elements("end_location").Select(p => new
{
Lat = p.Elements("lat").Select(q => q.Value).FirstOrDefault(),
Lng = p.Elements("lng").Select(q => q.Value).FirstOrDefault()
}).FirstOrDefault(),
Start_Address = o.Elements("start_address").Select(q => q.Value).FirstOrDefault(),
End_Address = o.Elements("end_address").Select(q => q.Value).FirstOrDefault()
}).ToList(),
Copyrights = n.Elements("copyrights").Select(q => q.Value).FirstOrDefault(),
Overview_polyline = n.Elements("overview_polyline").Select(q => new
{
Points = q.Elements("points").Select(r => r.Value).FirstOrDefault()
}).FirstOrDefault(),
Waypoint_Index = n.Elements("waypoint_index").Select(o => o.Value).ToList(),
Bounds = n.Elements("bounds").Select(q => new
{
SouthWest = q.Elements("southwest").Select(r => new
{
Lat = r.Elements("lat").Select(s => s.Value).FirstOrDefault(),
Lng = r.Elements("lng").Select(s => s.Value).FirstOrDefault()
}).FirstOrDefault(),
NorthEast = q.Elements("northeast").Select(r => new
{
Lat = r.Elements("lat").Select(s => s.Value).FirstOrDefault(),
Lng = r.Elements("lng").Select(s => s.Value).FirstOrDefault()
}).FirstOrDefault(),
}).FirstOrDefault()
}).FirstOrDefault()
}).FirstOrDefault();
I hope this will help someone.