I am writing a helloworld MonoTouch App to use ServiceStack to consume Json and have a two part related question.
My test json is: https://raw.github.com/currencybot/open-exchange-rates/master/latest.json
In my DTO object how to I use different named properties that map to json elements?
I have this, and it works, but I want to use different field names?
public class Currency
{
public string disclaimer { get; set; }
public string license { get; set; }
public string timestamp { get; set; }
}
And how do I add the Rates collection in my DTO from this json?
"rates": {
"AED": 3.6731,
"AFN": 48.330002,
"ALL": 103.809998,
ETC...
ServiceStack has an awesome Fluent JSON Parser API that makes it really easy to work against your existing model without having to use the "Contract" base serialization. This should get you started:
public class Rates {
public double AED { get; set; }
public double AFN { get; set; }
public double ALL { get; set; }
}
public class Currency {
public string disclaimer { get; set; }
public string license { get; set; }
public string timestamp { get; set; }
public Rates CurrencyRates { get; set; }
}
...
var currency = new Currency();
currency.CurrencyRates = JsonObject.Parse(json).ConvertTo(x => new Currency{
disclaimer = x.Get("disclaimer"),
license = x.Get("license"),
timestamp = x.Get("timestamp"),
CurrencyRates = x.Object("rates").ConvertTo(r => new Rates {
AED = x.Get<double>("AED"),
AFN = x.Get<double>("AFN"),
ALL = x.Get<double>("ALL"),
})
});
Related
I am trying to patch a object with the following code.
public object Patch(EditBlog request)
{
using (var db = _db.Open())
{
try
{
request.DateUpdated = DateTime.Now;
Db.Update<Blog>(request, x => x.Id == request.Id);
return new BlogResponse { Blog = Blog = Db.Select<Blog>(X=>X.Id == request.Id).SingleOrDefault()};
}
catch (Exception e)
{
return HttpError.Conflict("Something went wrong");
}
}
}
In Postman, I am calling the function like this "api/blog/1?=Title=Test1&Summary=Test&UserId=1".
When debugging I can see that those values has been assigned to the request.
During the Update it throws: "Cannot update identity column 'Id'"
My model looks like this
public class Blog
{
[AutoIncrement]
public int Id { get; set; }
public IUserAuth User { get; set; }
[Required]
public int UserId { get; set; }
[Required]
public string Title { get; set; }
public string Summary { get; set; }
public string CompleteText { get; set; }
[Required]
public DateTime DateAdded { get; set; }
public DateTime DateUpdated { get; set; }
}
And the EditBlog DTO looks like this:
[Route("/api/blog/{id}", "PATCH")]
public class EditBlog : IReturn<BlogResponse>
{
public int Id { get; set; }
public IUserAuth User { get; set; }
public int UserId { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string CompleteText { get; set; }
public DateTime DateUpdated { get; set; }
}
The error message "Cannot update identity column 'Id'" does not exist anywhere in ServiceStack.OrmLite, it could be an error returned by the RDBMS when you're trying to update the Primary Key which OrmLite wouldn't do when updating a Model annotated with a Primary Key like your Blog class has with its annotated [AutoIncrement] Id Primary Key.
The error is within your Db.Up<T> method that's performing the update, which is not an OrmLite API, so it's likely your own custom extension method or an alternative library.
I would implement a PATCH Request in OrmLite with something like:
var blog = request.ConvertTo<Blog>();
blog.DateUpdated = DateTime.Now;
Db.UpdateNonDefaults(blog);
i.e. using OrmLite's UpdateNonDefaults API to only update non default fields and updating using the Blog Table POCO not the EditBlog Request DTO.
Also you should use the Single APIs when fetching a single record, e.g:
Blog = Db.SingleById<Blog>(request.Id)
or
Blog = Db.Single<Blog>(x => x.Id == request.Id)
Instead of:
Blog = Db.Select<Blog>(X=>X.Id == request.Id).SingleOrDefault()
I have the following AutoQuery function.
[Route("/cars/search")]
public class SearchCars : QueryDb<Car, CarDto>
{
public List<int> EquipmentIds { get; set; }
public List<int> ManufacturerIds { get; set; }
public List<int> ColourIds { get; set; }
}
The function works, when I do the following:
Cars/Search?ColourIds=1&format=json
Cars/Search?ManufacturerIds=1&format=json
but when I try to use
Cars/Search?EquipmentIds=1&format=json
I get "Conversion failed when converting the varchar value '[1]' to data type int.".
The difference between these fields is that Car object can have multiple EquipmentIds, but only one ColourId and ManufacturerId.
public class Car
{
[AutoIncrement]
public int Id { get; set; }
public Colour Colour { get; set; }
[Required]
public int ColourId { get; set; }
public Manufacturer Manufacturer { get; set; }
[Required]
public int ManufacturerId { get; set; }
[Required]
public List<Equipment> Equipment { get; set; }
[Required]
public List<int> EquipmentId { get; set; }
}
Do I have to define for which attribute the different parameters should be assigned too?
AutoQuery works by constructing an RDBMS query based on implicit conventions which is used to construct an SQL query that runs on the RDBMS.
Complex Types in OrmLite data models are blobbed by default which means they can't be queried in the RDBMS with SQL, so you wont be able to query it with AutoQuery.
You could create a hybrid Custom AutoQuery Implementation where you can apply any custom logic to filter the results of the AutoQuery results, something like...
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
//Override with custom implementation
public object Any(SearchCars query)
{
var equipmentIds = query.EquipmentIds;
query.EquipmentIds = null;
var q = AutoQuery.CreateQuery(query, base.Request);
var response = AutoQuery.Execute(query, q);
if (equipmentIds != null)
response.Results.RemoveAll(x => x.EquipmentId...);
return response.
}
}
ServiceStack's AutoQuery Viewer Plugin allows you to decorate the AutoQueries using AutoQuery metadata attributes. I use the existing Metadata service in AutoQuery to power a front-end and display search queries (similar to the existing AutoQuery Admin Feature)
How can I extend/ add additional properties to the AutoQueryViewerAttribute, such that they are available in the Autoquery metadata service?
Current list of AutoQuery attributes available:
public class AutoQueryViewerAttribute : AttributeBase
{
public string Title { get; set; }
public string Description { get; set; }
public string IconUrl { get; set; }
public string BrandUrl { get; set; }
public string BrandImageUrl { get; set; }
public string TextColor { get; set; }
public string LinkColor { get; set; }
public string BackgroundColor { get; set; }
public string BackgroundImageUrl { get; set; }
public string DefaultSearchField { get; set; }
public string DefaultSearchType { get; set; }
public string DefaultSearchText { get; set; }
public string DefaultFields { get; set; }
}
I would like to extend the list of AutoQueryViewerAttribute attributes and add two additional properties:
public string SourceDescription { get; set; }
public string SourceApplicationName { get; set; }
You can't extend the [AutoQueryViewer] attribute which is hard coded. The Info on the Attribute is used to populate the Typed AutoQueryMetadataResponse DTO which is what's serialized to provide the AutoQuery metadata services. I've just added Meta String Dictionaries on the MetadataType, AutoQueryViewerConfig, AutoQueryViewerUserInfo, AutoQueryOperation and AutoQueryMetadataResponse DTO in this commit so you can attach additional metadata to the AutoQuery metadata DTOs using the MetadataFilter, e.g:
Plugins.Add(new AutoQueryMetadataFeature {
MetadataFilter = response => {
response.Meta = new Dictionary<string,string> {
{ "SourceApplicationName", "My App" },
{ "SourceDescription", "My App Description" },
};
}
});
This change is available from v4.5.13 that's now available on MyGet.
I need to make the following call to an open API (https://www.openfigi.com/api)
Curl Example:
curl -v -X POST 'https://api.openfigi.com/v1/mapping' \
--header 'Content-Type: text/json' \
--data '[{"idType":"ID_WERTPAPIER","idValue":"851399","exchCode":"US"}]'
Request Format
The request is passed in via HTTP request body. The only supported HTTP verb is POST. Here is a sample request to the API:
[
{"idType":"ID_ISIN","idValue":"US4592001014"},
{"idType":"ID_WERTPAPIER","idValue":"851399","exchCode":"US"},
{"idType":"ID_BB_UNIQUE","idValue":"EQ0010080100001000","currency": "USD"},
{"idType":"ID_SEDOL","idValue":"2005973","micCode":"EDGX", "currency":"USD"}
]
Using ServiceStack Request DTO, how do I make a RequestDto to achieve a call to to the above third party service endpoint.
This is just an exercise of creating DTOs which match the shape of the JSON you want to output and JSON you want to receive. To emit the exact the exact JSON property names you can either use [DataMember] on the Request DTO, or JsConfig.EmitCamelCaseNames = true to tell ServiceStack to serialize properties in camelCase or you can use JsConfig.With() to create a Custom Scope.
I've created a Live example of this in Gistlyn which you can use to experiment against Bloomberg's API.
I've used [DataMember] attribute here as it will work independent of your Json Serialization config. You don't need to do this for the Response DTO because ServiceStack Serializers is case-insensitive.
So to send the Request that matches the shape of that JSON you can use:
[DataContract]
public class Mapping
{
[DataMember(Name="idType")]
public string IdType { get; set; }
[DataMember(Name="idValue")]
public string IdValue { get; set; }
[DataMember(Name="exchCode")]
public string ExchCode { get; set; }
[DataMember(Name="currency")]
public string Currency { get; set; }
[DataMember(Name="micCode")]
public string MicCode { get; set; }
}
You can use ServiceStack's HTTP Utils to easily send requests to 3rd Party APIs, e.g:
var url = "https://api.openfigi.com/v1/mapping";
var json = url.PostJsonToUrl(new[]{
new Mapping { IdType = "ID_ISIN", IdValue = "US4592001014" },
new Mapping { IdType = "ID_WERTPAPIER", IdValue = "851399", ExchCode = "US" },
new Mapping { IdType = "ID_BB_UNIQUE", IdValue = "EQ0010080100001000", Currency = "USD" },
new Mapping { IdType = "ID_SEDOL", IdValue = "2005973", MicCode = "EDGX", Currency = "USD" },
});
Then to receive the response you need to create DTOs which match the shape of the JSON Response which looks like:
public class BloombertResult
{
public string Figi { get; set; }
public string SecurityType { get; set; }
public string MarketSector { get; set; }
public string Ticker { get; set; }
public string Name { get; set; }
public string UniqueId { get; set; }
public string ExchCode { get; set; }
public string ShareClassFIGI { get; set; }
public string CompositeFIGI { get; set; }
public string SecurityType2 { get; set; }
public string SecurityDescription { get; set; }
public string UniqueIdFutOpt { get; set; }
}
public class BloombergResponse
{
public List<BloombertResult> Data { get; set; }
public string Error { get; set; }
}
Which you can just deserialize into a collection of BloombergResponse, e.g:
var response = json.FromJson<BloombergResponse[]>();
Gistlyn will show you a nice human readable preview of each variable by clicking on it in the watch window. Or if you're this in a C# Unit test you can quickly see to populated DTOs with:
response.PrintDump();
I've subscribed to the Nexmo SMS service and they offer a callback URL for inbound SMS. The post request gives the following Json structure when notifying of SMS receipt:
{
"msisdn": "441632960960",
"to": "441632960961",
"messageId": "02000000E68951D8",
"text": "Hello7",
"type": "text",
"keyword": "HELLO7",
"message-timestamp": "2016-07-05 21:46:15"
}
Using the following code snippet, I can map all of the fields to my SmsReceipt apart from 'message-timestamp'. None of the message timestamp fields are populated.
public class SmsReceipt
{
public string msisdn { get; set; }
public string to { get; set; }
public string messageId { get; set; }
public string text { get; set; }
public string type { get; set; }
public string keyword { get; set; }
public string messagetimestamp { get; set; }
public string messageTimestamp { get; set; }
public string message_timestamp { get; set; }
}
[HttpPost("inboundsms")]
public async Task<IActionResult> Post([FromBody] SmsReceipt receipt)
{
return StatusCode(200);
}
I guess the same applies to incoming requests with other special characters such as '.' Any ideas greatly appreciated.
Your property name should match with the property name in the data being sent. Looks like your payload property name is message-timestamp. You cannot create a C# property with a - in it. So your options are
Either update your json payload property to match with one from your C# class.
Decorate your C# class with JsonProperty(From Newtonsoft.Json) where you specify what property from the posted data should be mapped to this property.
Also i suggest use the DateTime type. That type was created to deal with date time value.
public class SmsReceipt
{
public string Msisdn { get; set; }
public string To { get; set; }
public string MessageId { get; set; }
public string Text { get; set; }
public string Type { get; set; }
public string Keyword { get; set; }
[JsonProperty("message-timestamp")]
public DateTime Messagetimestamp { get; set; }
}