NancyFX Post - Bind to Object with List Parameter - nancy

I am using Nancy and I have created a basic model with a List property on it. When I use the below GET method I get the expected JSON output. However when I try to POST back the same JSON I end up with an object with and empty list of strings. I created two properties just to verify that my issue wasn't because of instantiating the DataPoints parameter to a new List in the constructor. Does anyone have any ideas on why this isn't returning properly?
JSON Object Returned from Initial Get
{
"dataPoints": [
"0",
"1",
"2",
"3",
"4"
],
"dataPoints2": [
"0",
"1",
"2",
"3",
"4"
]
}
JSON Object Returned from Post:
{
"dataPoints": [],
"dataPoints2": []
}
Class:
public class BasicModel
{
private List<string> _dataPoints;
public List<string> DataPoints
{
get
{
if (_dataPoints == null)
{
_dataPoints = new List<string>();
}
return _dataPoints;
}
set
{
_dataPoints = value;
}
}
public List<string> DataPoints2 { get; set; }
public BasicModel()
{
DataPoints2 = new List<string>();
}
public BasicModel(int idx)
{
DataPoints2 = new List<string>();
for (int i = 0; i < idx; i++)
{
DataPoints.Add(i.ToString());
DataPoints2.Add(i.ToString());
}
}
}
Get Method:
Get["/basicModel/{idx}"] = parameters =>
{
int idx = Convert.ToInt32(parameters["idx"].ToString());
BasicModel m = new BasicModel(idx);
return Negotiate
.WithStatusCode(HttpStatusCode.OK)
.WithModel(m)
.WithView("default");
};
Post Method:
Post["/basicmodel"] = parameters =>
{
BasicModel m = new BasicModel();
this.BindTo(m);
return Negotiate
.WithStatusCode(HttpStatusCode.OK)
.WithModel(m)
.WithView("default");
};

You may be running into a know issue with BindTo create a new instance even though you give one.
You should be able use Bind instead of BindTo though and have bind return a new object:
Post["/basicmodel"] = parameters =>
{
BasicModel m = this.Bind();
return Negotiate
.WithStatusCode(HttpStatusCode.OK)
.WithModel(m)
.WithView("default");
};

Related

How do i get array of dictionary's 2nd value from JSON response, in Swift

This is my JSON response in postman:
{
"result": {
"reviewcat": [
{
"id": 5
},
{
"id": 6
},
{
"id": 7
}
],
"reviewDet": {
"review_details": [
{
"review_point": "2.0"
},
{
"review_point": "4.5"
},
{
"review_point": "3",
}
],
for above response i have created model like below
public class BuyerReviewModel {
public var result : BuyserReviewResult?
}
public class BuyserReviewResult {
public var reviewcat : Array<Reviewcat>?
public var reviewDet : ReviewDet?
}
public class Reviewcat {
public var review_name : String?
public var id : Int?
}
public class ReviewDet {
public var review_details : Array<Review_details>?
}
public class Review_details {
public var review_cat_id : Int?
public var review_point : Float?
}
code: with this code i am getting only first review review_point val 2.5...how do i get 2nd review_point value 4.5 how?, could anybody guide me
class PostReviewVC: UIViewController {
#IBOutlet var rateImageViews: [UIImageView]!// 5 images array
#IBOutlet var rateButtons: [UIButton]!// 5 buttons array
var rateFatched: Float = 0.0
private var testData = ReviewModel(dictionary: NSDictionary()){
didSet{
titleLbl.text = byuReviewData?.result?.enquiry?.comments
idLbl.text = byuReviewData?.result?.enquiry?.ref_no
userNameLbl.text = "\(byuReviewData?.result?.reviewDet?.get_to_user?.first_name ?? "") \(byuReviewData?.result?.reviewDet?.get_to_user?.last_name ?? "")"
if let data = testData?.result?.reviewDet{
for buyReviewData in data.review_details ?? []{
rateFatched = buyReviewData.review_point ?? 0.0
if rateFatched == NSString("2.5").floatValue{
for button in rateButtons {
if button.tag >= 2 {
allImages[button.tag].image = (#imageLiteral(resourceName: "staremp"))
}
else {
allImages[button.tag].image = (#imageLiteral(resourceName: "star"))
}
}
}
}
}
}
}
}
Since the main issue here seems to be how to get the correct data to display here is solution that focus on that but ignores any UI related stuff since I consider it off-topic.
I did some changes to the structure but nothing major, changed from class to struct, removed some optionals that didn't make sense and changed from var to let but feel free to change it back since it isn't really important to the answer.
public struct BuyserReviewResult {
public let reviewcat : [Reviewcat]
public let reviewDet : ReviewDet?
}
public struct Reviewcat {
public let review_name : String
public let id : Int
}
public struct ReviewDet {
public let review_details : [Review_details]
}
public struct Review_details {
public let review_cat_id : Int
public let review_point : Float
}
Now you can simply get each category name and the points for that category by doing
for category in result.reviewcat {
let points = result.reviewDet?.review_details
.first(where: { $0.review_cat_id == category.id })?.review_point ?? 0.0
print(category.review_name, points)
}
Note that also ignored the top level type here since it is no longer needed once you have parsed the data.
Yes the review_point is of type String and not Float, as suggested by #burnsi
After getting the string value you can always parse it to Float

Deserialize JSON returned from external Url into an Object

Ok so my json return string looks like this:
{
"categories": [
{
"id": "1",
"category": "cat-1"
},
{
"id": "2",
"category": "cat-2"
},
{
"id": "3",
"category": "cat-3"
}
]
}
This list of returned categories is going to be used as a dropdown in my bootstrap nav menu so I would like to use the least amount of calls as possible as it will likely not change often enough to have to all it during each page refresh if I don't have to.
How would I write out my Model/ViewModel to bind to this string? I would then like to use something like this to return a list of CategoryViewModel from which I can iterate through.
public async Task<IEnumerable<CategoryViewModel>> GetCategoryList () {
HttpResponseMessage response = await httpClient.GetAsync ("/categories");
response.EnsureSuccessStatusCode ();
var result = await response.Content
.ReadAsAsync<IEnumerable<CategoryViewModel>> ();
return result;
}
The JSON model you have requires a container class, for example:
public class CategoryViewModelContainer
{
public IEnumerable<CategoryViewModel> Categories { get; set; }
}
//Assuming your category view model looks like this:
public class CategoryViewModel
{
public int Id { get; set; }
public string Category { get; set; }
}
And you use it like this:
var result = await response.Content.ReadAsAsync<CategoryViewModelContainer>();
Now you can loop through the categories:
foreach(var categoryModel in result.Categories)
{
var categoryName = categoryModel.Category;
}

How to add types information to JSON on serialization?

Angular requires Date objects in many places whereas JSON contains string representation of the date.
I want to add an array of properties which contain date values:
class Foo
{
public int IntProp {get;set;}
public DateTime? Prop1 {get;set;}
public DateTime Prop2 {get;set;}
public Bar Bar {set;set;}
}
class Bar
{
public DateTime Prop {get;set;}
public IEnumerable<DateTime?> Dates {get;set;}
}
Foo should then be serialized like this:
{
"IntProp": 1,
"Prop1": "...",
"Prop2": "...",
"Bar": {
"Prop": "..."
},
"<Dates>": [ "Prop1", "Prop2", "Bar.Prop", "Bar.Dates"]
}
This allows me to automatically convert strings to date objects at the client side without testing every property whether it is convertible to Date like it is described in this question.
I can collect the paths of date properties, but have no idea how to add populated array to the root.
You could convert to an intermediate JObject and add the property there. For instance, given the following converter:
public class PathLoggingDateTimeConverter : IsoDateTimeConverter
{
public const string DatePathPropertyName = "<Dates>";
readonly List<string> paths = new List<string>();
public override bool CanConvert(Type objectType)
{
if (!base.CanConvert(objectType))
return false;
// Not for DateTimeOffset
return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
base.WriteJson(writer, value, serializer);
if (value != null)
paths.Add(writer.Path);
}
public IList<string> Paths { get { return paths; } }
}
You can do:
var root = new Foo
{
IntProp = 101,
Prop1 = DateTime.Today.ToUniversalTime(),
Prop2 = DateTime.Today.ToUniversalTime(),
Bar = new Bar
{
Prop = DateTime.Today.ToUniversalTime(),
Dates = new List<DateTime?> { null, DateTime.Today.ToUniversalTime() },
},
};
var converter = new PathLoggingDateTimeConverter();
var settings = new JsonSerializerSettings { Converters = new[] { converter } };
var obj = JObject.FromObject(root, JsonSerializer.CreateDefault(settings));
obj[PathLoggingDateTimeConverter.DatePathPropertyName] = JToken.FromObject(converter.Paths);
Console.WriteLine(obj);
And the result is:
{
"IntProp": 101,
"Prop1": "2016-10-25T04:00:00Z",
"Prop2": "2016-10-25T04:00:00Z",
"Bar": {
"Prop": "2016-10-25T04:00:00Z",
"Dates": [
null,
"2016-10-25T04:00:00Z"
]
},
"<Dates>": [
"Prop1",
"Prop2",
"Bar.Prop",
"Bar.Dates[1]"
]
}

Single element array in WCF RESTful JSON web service client

I'm trying to consume a RESTful JSON web service using WCF on the client side. The service is 3rd party, so I cannot make any changes to the server response.
The server is sending back a response that looks something like this when there's only one data point...
Single Data Point
{
"Data":
{
"MyPropertyA":"Value1",
"MyPropertyB":"Value2"
},
}
and something like this when there's more than one data point...
Multiple Data Points
{
"Data":
[
{
"MyPropertyA":"Value1",
"MyPropertyB":"Value2"
},
{
"MyPropertyA":"Value3",
"MyPropertyB":"Value4"
},
{
"MyPropertyA":"Value5",
"MyPropertyB":"Value6"
}
],
}
I have my service contract set up like this...
[ServiceContract]
public interface IRewardStreamService
{
[OperationContract]
[WebInvoke]
MyResponse GetMyStuff();
}
and a data point's data contract like this...
[DataContract]
public class MyData
{
[DataMember]
public string MyPropertyA { get; set; }
[DataMember]
public string MyPropertyB { get; set; }
}
and the only way I can get the single data point response to work is if I have a single instance property like this, but this does not parse the multiple data point response...
Response for Single Instance
[DataContract]
public class MyResponse
{
[DataMember]
public MyData Data { get; set; }
}
and the only way I can get the multiple data point response to work is if I have an array / list instance property like this, but this does not parse the single data point response...
Response for Multiple Instance
[DataContract]
public class MyResponse
{
[DataMember]
public IList<MyData> Data { get; set; }
}
I understand the issue is that the response is omitting the brackets when there's only one data point returned, but it seems that WCF doesn't play well with deserializing that syntax. Is there some way I can tell the DataContractJsonSerializer to allow single element arrays to not include brackets and then tell my service to use that serializer? Maybe a service behavior or something?
Any direction would be helpful.
You can use a custom message formatter to change the deserialization of the JSON into the data contract you want. In the code below, the data contract is defined to have a List<MyData>; if the response contains only one data point, it will "wrap" that into an array prior to passing to the deserializer, so it will work for all cases.
Notice that I used the JSON.NET library to do the JSON modification, but that's not a requirement (it just has a nice JSON DOM to work with the JSON document).
public class StackOverflow_12825062
{
[ServiceContract]
public class Service
{
[WebGet]
public Stream GetData(bool singleDataPoint)
{
string result;
if (singleDataPoint)
{
result = #"{
""Data"":
{
""MyPropertyA"":""Value1"",
""MyPropertyB"":""Value2""
},
}";
}
else
{
result = #"{
""Data"":
[
{
""MyPropertyA"":""Value1"",
""MyPropertyB"":""Value2""
},
{
""MyPropertyA"":""Value3"",
""MyPropertyB"":""Value4""
},
{
""MyPropertyA"":""Value5"",
""MyPropertyB"":""Value6""
}
],
} ";
}
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
return new MemoryStream(Encoding.UTF8.GetBytes(result));
}
}
[DataContract]
public class MyData
{
[DataMember]
public string MyPropertyA { get; set; }
[DataMember]
public string MyPropertyB { get; set; }
}
[DataContract]
public class MyResponse
{
[DataMember]
public List<MyData> Data { get; set; }
public override string ToString()
{
return string.Format("MyResponse, Data.Length={0}", Data.Count);
}
}
[ServiceContract]
public interface ITest
{
[WebGet]
MyResponse GetData(bool singleDataPoint);
}
public class MyResponseSingleOrMultipleClientReplyFormatter : IClientMessageFormatter
{
IClientMessageFormatter original;
public MyResponseSingleOrMultipleClientReplyFormatter(IClientMessageFormatter original)
{
this.original = original;
}
public object DeserializeReply(Message message, object[] parameters)
{
WebBodyFormatMessageProperty messageFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name];
if (messageFormat.Format == WebContentFormat.Json)
{
MemoryStream ms = new MemoryStream();
XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(ms);
message.WriteMessage(jsonWriter);
jsonWriter.Flush();
string json = Encoding.UTF8.GetString(ms.ToArray());
JObject root = JObject.Parse(json);
JToken data = root["Data"];
if (data != null)
{
if (data.Type == JTokenType.Object)
{
// single case, let's wrap it in an array
root["Data"] = new JArray(data);
}
}
// Now we need to recreate the message
ms = new MemoryStream(Encoding.UTF8.GetBytes(root.ToString(Newtonsoft.Json.Formatting.None)));
XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max);
Message newMessage = Message.CreateMessage(MessageVersion.None, null, jsonReader);
newMessage.Headers.CopyHeadersFrom(message);
newMessage.Properties.CopyProperties(message.Properties);
message = newMessage;
}
return this.original.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
throw new NotSupportedException("This formatter only supports deserializing reply messages");
}
}
public class MyWebHttpBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
IClientMessageFormatter result = base.GetReplyClientFormatter(operationDescription, endpoint);
if (operationDescription.Messages[1].Body.ReturnValue.Type == typeof(MyResponse))
{
return new MyResponseSingleOrMultipleClientReplyFormatter(result);
}
else
{
return result;
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WebHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior());
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetData(false));
Console.WriteLine(proxy.GetData(true));
Console.Write("Press ENTER to close the host");
((IClientChannel)proxy).Close();
factory.Close();
Console.ReadLine();
host.Close();
}
}
I don't know about using WCF so I'll change to Asp.Net WCF. Here is an article that will get you one the way
http://www.west-wind.com/weblog/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing
I just can't figure out how to determine if it's an array or a single object. Here is a little code.
[TestMethod]
public void SingleObject()
{
using (var client = new HttpClient())
{
var result = client.GetStringAsync("http://localhost:8080/api/JSONTestOne");
string content = result.Result;
JObject jsonVal = JObject.Parse(content);
dynamic aFooObj = jsonVal;
Console.WriteLine(aFooObj.afoo.A);
}
}
[TestMethod]
public void ArrayWithObject()
{
using (var client = new HttpClient())
{
var result = client.GetStringAsync("http://localhost:8080/api/JSONTest");
string content = result.Result;
JObject jsonVal = JObject.Parse(content);
dynamic foos = jsonVal;
Console.WriteLine(foos[0].A);
}
}

How can I modify my classes to use it's collections in WPF TreeView

i'am trying to modify my objects to make hierarchical collection model. I need help. My objects are Good and GoodCategory:
public class Good
{
int _ID;
int _GoodCategory;
string _GoodtName;
public int ID
{
get { return _ID; }
}
public int GoodCategory
{
get { return _GoodCategory; }
set
{
_GoodCategory = value;
}
}
public string GoodName
{
get { return _GoodName; }
set
{
_GoodName = value;
}
}
public Good(IDataRecord record)
{
_ID = (int)record["ID"];
_GoodtCategory = (int)record["GoodCategory"];
}
}
public class GoodCategory
{
int _ID;
string _CategoryName;
public int ID
{
get { return _ID; }
}
public string CategoryName
{
get { return _CategoryName; }
set
{
_CategoryName = value;
}
}
public GoodCategory(IDataRecord record)
{
_ID = (int)record["ID"];
_CategoryName = (string)record["CategoryName"];
}
}
And I have two Collections of these objects:
public class GoodsList : ObservableCollection<Good>
{
public GoodsList()
{
string goodQuery = #"SELECT `ID`, `ProductCategory`, `ProductName`, `ProductFullName` FROM `products`;";
using (MySqlConnection conn = ConnectToDatabase.OpenDatabase())
{
if (conn != null)
{
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = productQuery;
MySqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Add(new Good(rdr));
}
}
}
}
}
public class GoodCategoryList : ObservableCollection<GoodCategory>
{
public GoodCategoryList ()
{
string goodQuery = #"SELECT `ID`, `CategoryName` FROM `product_categoryes`;";
using (MySqlConnection conn = ConnectToDatabase.OpenDatabase())
{
if (conn != null)
{
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = productQuery;
MySqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Add(new GoodCategory(rdr));
}
}
}
}
}
So I have two collections which takes data from the database. But I want to use thats collections in the WPF TreeView with HierarchicalDataTemplate. I saw many post's with examples of Hierarlichal Objects, but I steel don't know how to make my objects hierarchicaly. Please help.
Add a collection property to the "parent" class (I guess GoodCategory). You might make this an IList<ChildType> or an ObservableCollection<ChildType>. (Or, if you don't want consuming code to be able to add Goods to a GoodCategory, use a read-only collection.) For example:
class GoodCategory
{
private ObservableCollection<Good> _goods = new ObservableCollection<Good>();
public ObservableCollection<Good> Goods
{
get { return _goods; }
}
}
You will need to ensure that this collection is properly synchronised with the Good.GoodCategory property -- for example, when the Good.GoodCategory property changes a Good might remove itself from its existing GoodCategory.Goods collection and add itself to the new GoodCategory's Goods collection. If you use an object-relational mapper rather than handcrafted classes and SQL statements than the ORM should take care of this for you.

Resources