Process XML output from API - c#

I am getting an XML return from an Ebay API call. This is actually an Ebay category list of collections. But the problem is, I can't access its collection from XML output. I have attached two pictures - the first one showing debug of XML value returning variable, and the second one showing "InnerList". My main goal is prepare this XML data to store on my database, so I need a clean list of values from XML data. Any ideas?

You could deserialize your xml into your own class/object - Then it might be easier to work with. All i do is put xml tags to a class and i can deserialize it. See the class and method below:
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
[XmlElement("adress")]
public class Adress
{
[XmlElementAttribute("street_address")]
public string street_address { get; set; }
[XmlElementAttribute("postal_code")]
public string postal_code { get; set; }
[XmlElementAttribute("city")]
public string city { get; set; }
[XmlElementAttribute("country")]
public string country { get; set; }
}
public main()
{
Adress myAdress = Deserialize<Adress>(XMLstring);
}
Hope it helps!

It seems you are using Ebay SDK. Please try code below to process return values.
foreach (CategoryTypeCollection item in categories)
{
item.ItemAt(0).CategoryID = "This is how you access the properties of he returned result";
// THE XML is already parsed for you via SDK, so you don't have to parse it...
// since i wrote foreach loop here, always access itemAt 0th index posiiton
}

Related

How to fix error Cannot deserialize the current JSON object? [duplicate]

Below is a (slightly) stripped down response I get from a REST API upon successful creation of a new "job code" entry. I need to deserialize the response into some classes, but I'm stumped.
For reference, I'm using JSON.NET in .NET 3.5 (running in a SSIS script in SQL Server 2008 R2) to attempt my deserialization. Here's the JSON - which I obviously have no control over as it's coming from someone else's API:
{
"results":{
"jobcodes":{
"1":{
"_status_code":200,
"_status_message":"Created",
"id":444444444,
"assigned_to_all":false,
"billable":true,
"active":true,
"type":"regular",
"name":"1234 Main Street - Jackson"
},
"2":{
"_status_code":200,
"_status_message":"Created",
"id":1234567890,
"assigned_to_all":false,
"billable":true,
"active":true,
"type":"regular",
"name":"4321 Some Other Street - Jackson"
}
}
}
}
In my C# code, I do have a "JobCode" class defined which only partially maps the JSON values to properties - I'm not interested in all of the data that's returned to me:
[JsonObject]
class JobCode
{
[JsonProperty("_status_code")]
public string StatusCode { get; set; }
[JsonProperty("_status_message")]
public string StatusMessage { get; set; }
[JsonProperty("id")]
public string Id {get; set;}
[JsonProperty("name")]
public string Name { get; set; }
//-------------------------------------------------------------------------------
// Empty constructor for JSON serialization support
//-------------------------------------------------------------------------------
public JobCode() { }
}
I'm attempting to deserialize the data via this call:
newResource = JsonConvert.DeserializeObject<JobCode>(jsonResponse);
Where jsonResponse is the code outputted above.
When I execute the code, "newResource" always comes back as null - which is not unexpected because I know that there are actually multiple jobcodes in the data and this code is trying to deserialize it into a single JobCode object. I tried creating a new class called "JobCodes" that looks like this:
class JobCodes
{
[JsonProperty("jobcodes")]
public List<JobCode>_JobCodes { get; set; }
}
And then I tried calling this:
newResource = JsonConvert.DeserializeObject<JobCodes>(jsonResponse);
But the issue persists - my return object is null.
What's throwing me off, I think, is the presence of the "1" and "2" identifiers. I don't know how to account for their presence in my object design and/or usage of the JSON.NET class / property attributes like [JsonObject],[JsonProperty], etc.
When I run the JSON data through JSON2CSharp, it constructs some weird-looking classes, so that hasn't proven too effective. I've validated the JSON with several different validators and it all checks out - I just don't know what I'm missing here.
Ultimately, I'd like to return a List from the JSON data, but I'm stumped on what I need to do to make that happen.
Your problem is twofold:
You don't have a class defined at the root level. The class structure needs to match the entire JSON, you can't just deserialize from the middle.
Whenever you have an object whose keys can change, you need to use a Dictionary<string, T>. A regular class won't work for that; neither will a List<T>.
Make your classes like this:
class RootObject
{
[JsonProperty("results")]
public Results Results { get; set; }
}
class Results
{
[JsonProperty("jobcodes")]
public Dictionary<string, JobCode> JobCodes { get; set; }
}
class JobCode
{
[JsonProperty("_status_code")]
public string StatusCode { get; set; }
[JsonProperty("_status_message")]
public string StatusMessage { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
Then, deserialize like this:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
Working demo here
Excellent Answers!
For those out there that may need some more help with the JSON Class Configuration, try: http://json2csharp.com/#
An excellent way of Auto Generating the Classes!
Or even easier, in VS, Goto:
Edit -> Paste Special -> Paste as JSON Classes
Because you can't change the scheme of JSON, and you can't set constant No. of properties, I'd suggest you to use JObject
var jobject = JObject.Parse(json);
var results = jobject["results"];
var jobcodes = results["jobcodes"];
var output = jobcodes.Children<JProperty>()
.Select(prop => prop.Value.ToObject<JobCode>())
.ToList();
Warning: code assumes, that JSON is always in proper schema. You should also handle invalid schema (for example where property is not of JobCode scheme).
You can also deserialize your json to an object of your target class, and then read its properties as per normal:
var obj = DeSerializeFromStrToObj<ClassToSerialize>(jsonStr);
Console.WriteLine($"Property: {obj.Property}");
where DeSerializeFromStrToObj is a custom class that makes use of reflection to instantiate an object of a targeted class:
public static T DeSerializeFromStrToObj<T>(string json)
{
try
{
var o = (T)Activator.CreateInstance(typeof(T));
try
{
var jsonDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
var props = o.GetType().GetProperties();
if (props == null || props.Length == 0)
{
Debug.WriteLine($"Error: properties from target class '{typeof(T)}' could not be read using reflection");
return default;
}
if (jsonDict.Count != props.Length)
{
Debug.WriteLine($"Error: number of json lines ({jsonDict.Count}) should be the same as number of properties ({props.Length})of our class '{typeof(T)}'");
return default;
}
foreach (var prop in props)
{
if (prop == null)
{
Debug.WriteLine($"Error: there was a prop='null' in our target class '{typeof(T)}'");
return default;
}
if (!jsonDict.ContainsKey(prop.Name))
{
Debug.WriteLine($"Error: jsonStr does not refer to target class '{typeof(T)}'");
return default;
}
var value = jsonDict[prop.Name];
Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object safeValue = value ?? Convert.ChangeType(value, t);
prop.SetValue(o, safeValue, null); // initialize property
}
return o;
}
catch (Exception e2)
{
Debug.WriteLine(e2.Message);
return o;
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return default;
}
}
A complete working example class can be found in my enhanced answer to a similar question, here

How can I loop through the JSON on Universal Windows Platform with C#

I am developing an app on UWP.
When I connect with a server api and I get the next response I don't have problems.
{"value":"Login successfull","sessionId":"a95077855b05ed0fec5d7fa3abafa126e15aba2a"}
I can get information in the following way:
JsonObject jsonObject = JsonObject.Parse(jsonString);
string token = jsonObject["sessionId"].GetString();
string value = jsonObject["value"].GetString();
but my problem is when i get the next response of the api:
[{"person":{"name":"name1","country":"Spain","city":"user_city","phone":null}},{"person":{"name":"name2","country":"Turkey","city":"user_city","phone":"1111111"}},{"person":{"name":"name3","country":"Argentina","city":"user_city","phone":"22222"}},{"person":{"name":"name4","country":"Argentina","city":"user_city","phone":"33333"}}]
How can I loop through the JSON and get all the people that match a condition?
I have to do with "Windows.Data.Json"
If interested in a solution using only Windows.Data.Json namespace, here it is:
var rootValue = JsonValue.Parse(jsonString);
foreach (var item in rootValue.GetArray())
{
var unamedObject = item.GetObject();
var personObject = unamedObject["person"].GetObject();
System.Diagnostics.Debug.WriteLine(personObject["name"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["country"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["city"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["phone"].GetString());
}
Why would somebody pick Windows.Data.Json over Newtonsoft's Json.net?
If your JSON needs are simple, you can reduce the size of your app ~1 MB by choosing Windows.Data.Json because it is part of the operating system.
I would recommend you try out Json.net nuget package and deserialise the json payload to classes through that.
A good tutorial can be found here: http://windowsapptutorials.com/windows-phone/general/deserialize-json-data-using-newtonsoft-json-net-library/
But if you search you'll find more.
In short, you first copy paste your json and use Visual Studio > File > Paste Special > To paste to classes ( first open an empty cs file and set your cursor inside it ).
After that you use JsonConvert.DeserializeObject<RootObject>() to actually parse the json string.
Once parsed you'll have an array of items if your original json also defined an array.
Note RootObject is the first class object in the generated classes in Visual Studio
There are ways to do it without external libraries, if that is the real reason for the stipulation of Windows.Data.Json.
I'd likely do it something like this...
First I'd make some classes representing the returning JSON:
public class RootObject
{
public Person person { get; set; }
}
public class Person
{
public string name { get; set; }
public string country { get; set; }
public string city { get; set; }
public string phone { get; set; }
}
Then add a little method to deserialize:
public static T Deserialize<T>(string json)
{
var bytes = Encoding.Unicode.GetBytes(json);
using (var ms = new MemoryStream(bytes))
{
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
And finally deserialize and query that result like so:
var persons = Json.Deserialize<List<RootObject>>(textBox.Text);
var peeps = (from p in persons
where p.person.name.StartsWith("name")
select p).ToList();

iterating through objects of response JSON

I'm doing a query and get as result something like this, which i put into a Hashtable
{"success":"true", "result":[{"type":"email", "address":"aaasd#asd.com"},{"type":"email", "address":"aaasddee#dse.com"}]}
then i do
return hashtable["result"];
so I only have this left
[{"type":"email", "address":"aaasd#asd.com"},{"type":"email", "address":"aaasddee#dse.com"}]
but my problem is that I don't know how to iterate through every object from "result" to fill my own objects. I was searching for a solution but the only answer I found was to use
foreach(DictionaryEntry entry in searchResult) {
//do something<br>
}
When I iterate through the Hashtable like this I can only use the properties entry.Key and entry.Value but I can't say which value for a specific key I need. Any suggestions are welcome.
You can get it using deserialising using JSON.NET as shown below :-
var result = JsonConvert.DeserializeObject<dynamic>(searchResult);
You can create your class like below :-
public class RootObject
{
public string type { get; set; }
public string address { get; set; }
}
For more information :-
http://james.newtonking.com/json/help/index.html?topic=html/SerializingJSON.htm
Create a class that matches the signature of the result collection like:
public class Result
{
public string Type { get; set; }
public string Address { get; set; }
}
Then use Json.NET to parse the result node into a List<Result>. There is plenty of documentation online on how to use the Json.NET library.
Hope that helps,
Rob

XmlArrayItem Deserialize to an empty List

I'm getting empty lists when I deserialize one of my List that has been concerted to XML.
I start out with my object GJurisdictions and serialize it to XML
<open511 xml:lang="en" xml:base="http://api.open511.info/" version="v0">
<jurisdictions>
<jurisdiction>
<id>my.id</id>
<languages>
<language>en</language>
</languages>
</jurisdiction>
</jurisdictions>
</open511>
This is exactly what I want but when I try to read it back in Languages is empty.
public class Jurisdiction
{
[XmlElement("id")]
[JsonProperty("id")]
public string Id { get; set; }
[XmlArray("languages")]
[XmlArrayItem("language")]
[JsonProperty("languages")]
public List<String> Languages { get; set; }
}
[XmlRoot("open511")]
public class JurisdictionsBase : Open511Base
{
private List<Jurisdiction> _jurisdictions;
[XmlArray("jurisdictions")]
[XmlArrayItem("jurisdiction")]
[JsonProperty("jurisdiction")]
public List<Jurisdiction> Jurisdictions
{
get { return _jurisdictions ?? (_jurisdictions = new List<Jurisdiction>()); }
set { _jurisdictions = value; }
}
}
The list of jurisdictions serializes perfectly and all the other properties (I edited for clarity) serialize perfectly but the language list is always empty, why?
Specification: http://open511.org/jurisdiction.html
Full Code: https://github.com/doublej42/Open511DotNet/blob/master/Open511DotNet/Jurisdiction.cs
So I didn't give enough information without reading the full source code. My problem was that somewhere else in my project I used some sample code from http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.readxml(v=vs.110).aspx
public void ReadXml (XmlReader reader)
{
personName = reader.ReadString();
}
Do not use the above code
it doesn't move the reader ahead correctly.
As mentioned here http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=EN-US&k=k(System.Xml.XmlReader.ReadString);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.1);k(DevLang-csharp)&rd=true
public void ReadXml(System.Xml.XmlReader reader)
{
_unit = reader.ReadElementContentAsString();
}

How to pass multidimensional list or tuple to C# Web Service

I'm working on a web service that needs to accept a collection with three values of different types. The values are
SkuNumber (integer),
FirstName (string),
LastName (string)
I want the web service to accept a list of 100 instances of these values but am not sure how to go about it. Do I use a multidimensional list or array? Maybe a tuple? Or can I just create a simple class structure and accept a list of that class?
This is all simple enough in a normal application, I'm just not sure how the app calling the web service would pass the data with any of the given options.
Can someone give me some pointers?
If a shared assembly is not feasible, you can always go with good ol' XML. It may not be the optimal solution and I'm sure plenty of users here will balk at the idea, but it is easy to support and relatively quick to implement, so it really depends on your individual situation and the skill level of the developers responsible for supporting the application.
The benefit to using XML here, is that the calling application can be written in almost any language on almost any platform, as long as it adheres to the XML structure.
The XML string should be easy enough to generate in the calling application, but the biggest downside here is that if you have a ton of data, the processing may take longer than desired -- on both ends of the service.
Here is a working sample if you want to give it a try:
public class Whatever
{
public int SkuNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[WebMethod]
public void HelloWorld(string xmlString)
{
//make all the node names + attribute names lowercase, to account for erroneous xml formatting -- leave the values alone though
xmlString = Regex.Replace(xmlString, #"<[^<>]+>", m => m.Value.ToLower(),RegexOptions.Multiline | RegexOptions.Singleline);
var xmlDoc = LoadXmlDocument(xmlString);
var listOfStuff = new List<Whatever>();
var rootNode = xmlDoc.DocumentElement;
foreach(XmlNode xmlNode in rootNode)
{
var whatever = new Whatever
{
FirstName = xmlNode["first_name"].InnerText,
LastName = xmlNode["last_name"].InnerText,
SkuNumber = Convert.ToInt32(xmlNode["sku_number"].InnerText)
};
listOfStuff.Add(whatever);
}
}
public static XmlDocument LoadXmlDocument(string xmlString)
{
//some extra stuff to account for URLEncoded strings, if necessary
if (xmlString.IndexOf("%3e%") > -1)
xmlString = HttpUtility.UrlDecode(xmlString);
xmlString = xmlString.Replace((char)34, '\'').Replace("&", "&").Replace("\\", "");
var xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = false;
xmlDocument.LoadXml(xmlString);
return xmlDocument;
}
Your XML would look like this:
<stuff_to_track>
<whatever>
<sku_number>1</sku_number>
<first_name>jimi</first_name>
<last_name>hendrix</last_name>
</whatever>
<whatever>
<sku_number>2</sku_number>
<first_name>miles</first_name>
<last_name>davis</last_name>
</whatever>
<whatever>
<sku_number>3</sku_number>
<first_name>david</first_name>
<last_name>sanborn</last_name>
</whatever>
<whatever>
<sku_number>4</sku_number>
<first_name>john</first_name>
<last_name>coltrane</last_name>
</whatever>
</stuff_to_track>
I also recommend validating the incoming XML, for both schema and data.
Create a class and accept a list of that class. Be sure to mark it as [Serializable].
[Serializable]
public class Whatever
{
public int SkuNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Best practice would be to define the class in an assembly that can be accessed by both the service and the project that calls it.
The trouble with a tuple or a multi-dimensional array is that the data you send doesn't have an inherent identity: you could stick any old thing in there. If you have a class, you are indicating that you are sending an Order or an Inquiry or a Coupon or whatever it is you are tracking. There's a level of meaning that goes along with it.
Just send what you want:
public class Whatever
{
public int SkuNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[WebMethod]
public void TakeList(List<Whatever> theList)
{
foreach (var w in theList)
{
}
}

Categories

Resources