How To Parse ODATA Error - c#

<?xml version="1.0" encoding="utf-8"?>
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code />
<m:message xml:lang="en-US">An error occurred while processing this request.</m:message>
<m:innererror>
<m:message>Exception has been thrown by the target of an invocation.</m:message>
<m:type>System.Reflection.TargetInvocationException</m:type>
<m:stacktrace></m:stacktrace>
<m:internalexception>
<m:message>An error occurred while executing the command definition. See the inner exception for details.</m:message>
<m:type>System.Data.Entity.Core.EntityCommandExecutionException</m:type>
<m:stacktrace></m:stacktrace>
<m:internalexception>
<m:message>Cannot insert duplicate key row in object 'XXXX' with unique index 'XXXXXX'. The duplicate key value is (XXXXX)</m:message>
<m:type>System.Data.SqlClient.SqlException</m:type>
<m:stacktrace></m:stacktrace>
</m:internalexception>
</m:internalexception>
</m:innererror>
</m:error>" System.Data.Services.Client.DataServiceClientException
I have this XML structure coming back from a WEB API 2 ODATA source. I need to parse this error at the server then pass a new error to the client.I have tried deserializing this to various Exception types, but the fact that Exception Implement IDICTIONARY stops that. How can I deserialize this into a strongly typed object?
If the above isn't easily down, I guess my other question would be what is best practice for handling these errors

My suggestion as to how to handle these error messages that you get, is to create a similar looking class structure instead of trying to serialize the information into an Exception object.
This allows for a bit more flexibility on your end, in case the source of your XML decides to change the structure of their error messages instead of basing it on the structure of an Exception, plus it removes the requirement to try and figure out how to deserialize an Exception object (which according to a few searches is a bit more complexer).
So using the tool in Visual Studio to convert XML text into C# classes (found under Edit => Paste Special => Paste XML as Classes), the below class structure is obtained.
The actual structure is modified from what that tool generates, since it likes to bloat the classes a lot.
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")]
[XmlRootAttribute(ElementName = "error", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", IsNullable = false)]
public partial class ODATAException
{
public string code { get; set; }
public ODATAErrorMessage message { get; set; }
public ODATAInternalException innererror { get; set; }
}
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")]
public partial class ODATAErrorMessage
{
[XmlAttributeAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/XML/1998/namespace")]
public string lang { get; set; }
[XmlTextAttribute()]
public string Value { get; set; }
}
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")]
public partial class ODATAInternalException
{
public string message { get; set; }
public string type { get; set; }
public string stacktrace { get; set; }
public ODATAInternalException internalexception { get; set; }
}
Utilizing this class structure, you can then easily deserialize the received XML into a strong typed object and use it further in whichever way you like.
using (TextReader sampleTextReader = new StringReader(txtInput.Text)) // Change this whereever you get the XML string from
{
XmlSerializer sampleXmlSeri = new XmlSerializer(typeof(ODATAException));
ODATAException newExc = sampleXmlSeri.Deserialize(sampleTextReader) as ODATAException;
if (newExc != null)
{
// Do something with the error
}
}
You are also now free to give the variables any names that are more descriptive to yourself/your team and use the XmlElement attributes to link your properties to the actual names inside the XML.
There is also a very interesting and good Q/A regarding making an exception serializable, that I would recommend reading as well:
What is the correct way to make a custom .NET Exception serializable?

Related

C# getting MongoDB BsonDocument to return as JSON

I asked a question a couple of days ago to collect data from MongoDB as a tree.
MongoDB create an array within an array
I am a newbie to MongoDB, but have used JSON quite substantially. I thought using a MongoDB to store my JSON would be a great benefit, but I am just experiencing immense frustration.
I am using .NET 4.5.2
I have tried a number of ways to return the output from my aggregate query to my page.
public JsonResult GetFolders()
{
IMongoCollection<BsonDocument> collection = database.GetCollection<BsonDocument>("DataStore");
PipelineDefinition<BsonDocument, BsonDocument> treeDocs = new BsonDocument[]
{
// my query, which is a series of new BsonDocument
}
var documentGroup = collection.Aggregate(treeDocs).ToList();
// Here, I have tried to add it to a JsonResult Data,
// as both documentGroup alone and documentGroup.ToJson()
// Also, loop through and add it to a List and return as a JsonResult
// Also, attempted to serialise, and even change the JsonWriterSettings.
}
When I look in the Immediate Window at documentGroup, it looks exactly like Json, but when I send to browser, it is an escaped string, with \" surrounding all my keys and values.
I have attempted to create a model...
public class FolderTree
{
public string id { get; set; }
public string text { get; set; }
public List<FolderTree> children { get; set; }
}
then loop through the documentGroup
foreach(var docItem in documentGroup)
{
myDocs.Add(BsonSerializer.Deserialize<FolderTree>(docItem));
}
but Bson complains that it cannot convert int to string. (I have to have text and id as a string, as some of the items are strings)
How do I get my MongoDB data output as Json, and delivered to my browser as Json?
Thanks for your assistance.
========= EDIT ===========
I have attempted to follow this answer as suggested by Yong Shun below, https://stackoverflow.com/a/43220477/4541217 but this failed.
I had issues, that the "id" was not all the way through the tree, so I changed the folder tree to be...
public class FolderTree
{
//[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
//public string id { get; set; }
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
public class FolderTreeChildren
{
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
Now, when I look at documentGroup, I see...
[0]: {Plugins.Models.FolderTree}
[1]: {Plugins.Models.FolderTree}
To be fair to sbc in the comments, I have made so many changes to get this to work, that I can't remember the code I had that generated it.
Because I could not send direct, my json result was handled as...
JsonResult json = new JsonResult();
json.Data = documentGroup;
//json.Data = JsonConvert.SerializeObject(documentGroup);
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return json;
Note, that I also tried to send it as...
json.Data = documentGroup.ToJson();
json.Data = documentGroup.ToList();
json.Data = documentGroup.ToString();
all with varying failures.
If I leave as documentGroup, I get {Current: null, WasFirstBatchEmpty: false, PostBatchResumeToken: null}
If I do .ToJson(), I get "{ \"_t\" : \"AsyncCursor`1\" }"
If I do .ToList(), I get what looks like Json in json.Data, but get an error of Unable to cast object of type 'MongoDB.Bson.BsonInt32' to type 'MongoDB.Bson.BsonBoolean'.
If I do .ToString(), I get "MongoDB.Driver.Core.Operations.AsyncCursor`1[MongoDB.Bson.BsonDocument]"
=========== EDIT 2 =================
As this way of extracting the data from MongoDB doesn't want to work, how else can I make it work?
I am using C# MVC4. (.NET 4.5.2)
I need to deliver json to the browser, hence why I am using a JsonResult return type.
I need to use an aggregate to collect from MongoDB in the format I need it.
My Newtonsoft.Json version is 11.0.2
My MongoDB.Driver is version 2.11.1
My method is the simplest it can be.
What am I missing?

mongoDB, strongly typed collection using C# oficial driver

I'm learning MongoDB and I want to try it with using C#. Is it possible to operate on strongly typed MongoDB documents using C# official MongoDB driver?
I have classes Album and Photo:
public class Album : IEnumerable<Photo>
{
[Required]
[BsonElement("name")]
public string Name { get; set; }
[Required]
[BsonElement("description")]
public string Description { get; set; }
[BsonElement("owner")]
public string Owner { get; set; }
[BsonElement("title_photo")]
public Photo TitlePhoto { get; set; }
[BsonElement("pictures")]
public List<Photo> Pictures { get; set; }
//rest of the class code
}
public class Photo : IEquatable<Photo>
{
[BsonElement("name")]
public string Name;
[BsonElement("description")]
public string Description;
[BsonElement("path")]
public string ServerPath;
//rest of the class code
}
I want to insert a new document into a collection albums in the test database. I don't want to operate on BsonDocument, but I would prefer to use strongly typed Album. I thought it would be something like:
IMongoClient client = new MongoClient();
IMongoDatabase db = client.GetDatabase("test");
IMongoCollection<Album> collection = database.GetCollection<Album>("album");
var document = new Album
{
Name = album.Name,
Owner = HttpContext.Current.User.Identity.Name,
Description = album.Description,
TitlePhoto = album.TitlePhoto,
Pictures = album.Pictures
};
collection.InsertOne(document);
But it gives me the following error:
An exception of type 'MongoDB.Driver.MongoCommandException' occurred
in MongoDB.Driver.Core.dll but was not handled in user code
Additional information: Command insert failed: error parsing element 0
of field documents :: caused by :: wrong type for '0' field, expected
object, found 0: [].
What am I doing wrong and if it's possible to achieve?
It looks like the driver is treating your object as a BSON array because it implements IEnumerable<Photo>. The database is expecting a BSON document instead. You'll get a similar error if you try to insert, for example, an Int32 into a collection.
Unfortunately, I don't know how to configure the serializer to treat your Album object as a BSON document. The static BsonSerializer.SerializerRegistry property shows the driver is choosing to use EnumerableInterfaceImplementerSerializer<Album,Photo> as Album's serializer by default.
Removing the IEnumerable<Photo> implementation from Album causes the driver to serialize with BsonClassMapSerializer<Album>, producing a BSON document. While it works, the downside is Album is no longer enumerable; application consumers will need to enumerate the Pictures property.
Adding the IEnumerable<Photo> implementation back in, then forcing the aforementioned serializer (using the [BsonSerializer(typeof(BsonClassMapSerializer<Album>))] attribute) results in:
System.MissingMethodException: No parameterless constructor defined for this object.
Based on the stack trace (referring to BsonSerializerAttribute.CreateSerializer), the object the message refers to appears to be something serialization-related, not the data objects themselves (I defined parameterless constructors for both). I don't know if there's a way around this problem with further configuration, or if the driver just won't allow an IEnumerable to be used this way.

Building json response

I'm working on a self hosting rest api used to monitor de status of several servers.
I was tasked that, when everything is working correctly, I should only return
{"response":"ok"}
But, when there's an error on queried server, or servers, I must return
{ "response" : [ {"agent":"<server>:<port>","port":"<port>" ,"Error":"<Description of the error>"} ] }
I was thinking on building a helper class to build object on this schema and returning them over the rest api
public class HelperErrorResponseClass
{
public string agent { get; set; }
public string port { get; set; }
public string Error { get; set; }
}
This is no problem, the issue is, how to deal when everything it ok. I have this Api response helper class
public class Response
{
public string response { get; set; }
}
But I'm seeing that I'll need to change the response property to List<HelperErrorResponseClass> in order to send the error response. Do you think that, if I stringify the List<HelperErrorResponseClass> object with Json.Net it will be returned in the desired format?
Edit: Forgot to add that, I-m using Web Api to build the rest service.
UDPATE:
After further research, I found a way to work this out.
Following this post, I was able to rewrite the helper classes like this
[DataContract]
[KnownType(typeof(List<HelperErrorResponseClass>))]
public class Response
{
[DataMember]
public object response { get; set; }
}
[DataContract]
public class HelperErrorResponseClass
{
[DataMember(EmitDefaultValue = false)]
public string agent { get; set; }
[DataMember(EmitDefaultValue = false)]
public string port { get; set; }
[DataMember(EmitDefaultValue = false)]
public string error { get; set; }
}
This work to fulfill my and my client needs... except for one little thing. When I get the result from a List, and given that I added the KnownTypes directive, my response is now this
{"response":[{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"},{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"}]}
Any idea how to get rid of that __type property of the response? make that it must be explicit to only return the declared properties of the helper class?
Simplest way to deal with this is to set the return type on the handling function to string, then you can check for errors and do something like;
//pseudo code to give an idea
if (errorsList.Count() > 0)
{
return JsonConvert.SerializeObject(errorsList);
}
else
{
return JsonConvert.SerializeObject(new Response("ok"));
}
Now this being said... Unless the people providing requirements aren't at all flexible you should just redo the design. How about just returning the errors array and the person calling the API can infer that if it's length is 0 then everything is working OK. Seems pretty straight forward, right? You could also just put all the properties on one object and those fields would just come back as null or empty strings. Or you could change you serializer settings to exclude them if they don't have a value.
Keep things simple and use an anonymous type.
if (condition)
{
return JsonConvert.SerializeObject(new { response = new { agent = "x", port = "y", error = "z" }});
}
else
{
return JsonConvert.SerializeObject(new { response = "ok"});
}
More info:
https://msdn.microsoft.com/en-us/library/bb397696.aspx
I personally don't think you need a Response class, especially that it is of object type. IMHO, you've overcomplicated the very simple issue that you have. It is not only the __type, but also other info like HelperErrorResponseClass:#AppCommonLib that isn't supposed to be there.
Another Issue you have is the incorrect name of the HelperErrorResponseClass class. This is not a helper class. It is a standard data-object class.
A helper class is a class filled with static methods. It is usually used to isolate a "useful" algorithm.
This is how I would do it:
I'd get rid of the Response class.
I'd use your original simple HelperErrorResponseClass class, but rename it to something more meaningful like ErrorDetails.
I'd return the response like this:
.
if (errorsList.Count() > 0) {
return JsonConvert.SerializeObject(new { response = errorsList});
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}
However, if you really want to stick to your updated solution, an easy way to get rid of the __type is simply removing it from the final serialized string:
if (errorsList.Count() > 0) {
string r = JsonConvert.SerializeObject(new { response = errorsList});
return r.Replace("__type", "");
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}

XML Element Selection

I am collecting XML data from a weather website xml file here.
I have created the following code, which finds the first instance of 'temp_c' and returns the value. This is the temperature at the current moment.
using (XmlReader reader = XmlReader.Create(new StringReader(weather)))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name.Equals("temp_c"))
{
reader.Read();
temp_c = reader.Value;
}
break;
}
}
}
return temp_c
This returns the value of the first instance in the XML file called "temp_c" to the string called "temp_c"
What I would now like to do is use the Element in the XML document called "period" and the element found with period called "fcttext". When "period = 0" it means "today", 1 = tomorrow, etc; and I am after the "fcttext_metric" data that is associated with that period value.
I've been struggling to work the examples and XML reading codes I've found into the current code that I have here. Can anyone please point me in the right direction?
Edit
Here is an example of the XML file:
<forecast>
<forecastday>
<period>0</period>
<fcttext_metric>Sunny</fcttext_metric>
<forecastday>
<period>1</period>
<fcttext_metric>Cloudy</fcttext_metric>
This ended up being more annoying that I originally expected, but you can create an object graph from the XML using a DataContractSerializer and a set of classes that match the XML you are reading.
First, you create your classes, with appropriate DataContract attributes.
[DataContract(Name = "response", Namespace = "")]
public class WeatherData
{
[DataMember(Name = "forecast")]
public Forecast Forecast { get; set; }
}
[DataContract(Name = "forecast", Namespace = "")]
public class Forecast
{
[DataMember(Name = "txt_forecast")]
public TxtForecast TxtForecast { get; set; }
}
[DataContract(Name = "txt_forecast", Namespace = "")]
public class TxtForecast
{
[DataMember(Name = "forecastdays")]
public ForecastDay[] ForecastDays { get; set; }
}
[DataContract(Name = "forecastday", Namespace = "")]
public class ForecastDay
{
[DataMember(Name = "period", Order = 1)]
public int Period { get; set; }
public string FctText { get; set; }
[DataMember(Name = "fcttext", EmitDefaultValue = false, Order = 5)]
private CDataWrapper FctTextWrapper
{
get { return FctText; }
set { FctText = value; }
}
}
Heres where it got complicated. The fcttext element is CDATA, which the DataContractSerializer doesn't support by default.
Using the wonderful answer available at Using CDATA with WCF REST starter kits, you create a CDataWrapper class. I won't repeat the code here (because that would be pointless), just follow the link.
You can see that I've already used the CDataWrapper class above in order to work with the fcttext element.
Once you've got the class structure setup you can use the following code to extract the information you were after. You're just navigating the object graph at this point, so you can use whatever C# you want (I've used a simple LINQ query to find Period 0 and print out the fcttext for it).
var s = new DataContractSerializer(typeof(WeatherData));
var reader = XmlReader.Create("http://api.wunderground.com/api/74e1025b64f874f6/forecast/conditions/q/zmw:00000.1.94787.xml");
var o = (WeatherData)s.ReadObject(reader);
var firstPeriod = o.Forecast.TxtForecast.ForecastDays.Where(a => a.Period == 0).Single();
Console.WriteLine(firstPeriod.FctText);
You can extend the classes as necessary to give you access to additional fields inside the XML. As long as the DataMember names match the XML fields it will all work.
Here's a short summary of some problems I ran into for anyone interested:
I had to set the Namespace attributes of all of the classes to the empty string, because the XML doesn't have any namespace info in it.
The Order attributes were important on the ForecastDay class. If they are omitted, the DataContractSerializer ends up not reading the fcttext field in at all (because it thinks fcttext should come first? not sure why to be honest).

How would I read into a 'nested' Json file with 'DataContractJsonSerializer' in C# .NET (win7 phone)?

I have an issue where if my json file looks like this
{ "Numbers": "45387", "Words": "space buckets"}
I can read it just fine, however if it looks like this:
{ "Main" :{ "Numbers": "45387", "Words": "space buckets"},
"Something" :{"Numbers": "12345", "Words": "Kransky"} }
I get no information back. I have no idea how to switch between Main and Something!
Loading a JSON with this 'nested' information using this code,
var ser = new DataContractJsonSerializer(typeof(myInfo));
var info = (myInfo)ser.ReadObject(e.Result);
// The class being using to hold my information
[DataContract]
public class myInfo
{
[DataMember(Name="Numbers")]
public int number
{ get; set; }
[DataMember(Name="Words")]
public string words
{ get; set; }
}
Causes the class to come back empty.
I've tried adding the group name to DataContract eg. [DataContract, Name="Main"] but this still causes the classes values to be empty.
I've also tried adding "main" to the serializer overloader eg. var ser = new DataContractJsonSerializer(typeof(myInfo), "Main");
This causes an error: Expecting element 'Main' from namespace ''.. Encountered 'Element' with name 'root', namespace ''.
I'd prefer to just use the supplied json reader. I have looked into json.NET but have found the documentation to be heavy on writing json and sparse with information about reading.
Surely I'm missing something simple here!
You could add a wrapper class:
[DataContract]
public class Wrapper
{
[DataMember]
public myInfo Main { get; set; }
[DataMember]
public myInfo Something { get; set; }
}
Now you could deserialize the JSON back to this wrapper class and use the two properties to access the values.

Categories