ServiceStack ORMLite JSON Deserialization multiple levels - servicestack

I've got a class containing a Dictionary like this:
Dictionary<string,object> Data
I put data on multiple levels into here, such as:
Data.Add("simple",4);
Data.Add("object",new Dictionary<string,object>{{"key",4}});
Data.Add("array",new List<string>{"a","b","c"});
Then I .Save() it with OrmLite, which is setup to use JSON instead of JSV:
SqliteDialect.Provider.StringSerializer = new JsonStringSerializer();
Looking inside the stored data, using SQL, it looks like perfectly valid JSON.
{
"simple": 4,
"object": {
"key": 4
},
"array": [
"a","b","c"
]
}
However, when I Load() the data back, it doesn't deserialize back to the original.
Here's a link to Gist Cafe, showing the problem:
https://gist.cafe/9ed2bbb3a6a0348f129c493887ae8170

By default JsonStringSerializer uses ServiceStack.Text Typed JSON Serializer which can't deserialize an object back to its original type since that type information is lost, all it sees is the runtime object Type which it leaves as a string since it doesn't know what other object it should deserialize to.
Which is why it's recommended that you serialize typed DTOs so the type information is preserved, e.g:
class CustomData
{
public int Simple { get; set; }
public Dictionary<string,int> Object { get; set; }
}
If you want to deserialize anonymous object data structures into untyped collections you can configure ServiceStack.Text to use JS Utils to handle deserializing arbitrary JSON into untyped data structures by registering it with:
ServiceStack.JS.Configure();
Which I've done in this modified gist:
https://gist.cafe/953f2da51a5077ecf7b5109c14ec2c32
Which outputs the expected result:
{
Id: 1,
Data:
{
simple: 4,
object:
{
key: 4
},
array:
[
a,
b,
c
]
}
}
Whilst it works in this instance because you're using only serializing JSON data types, it wont work if your object dictionary contains complex types because all JS Utils can see is the raw JSON string without any type information, so it's only able to deserialize into untyped collections that matches the JSON object - which is why it's recommended to serialize Typed DTOs so the types are preserved.

Related

ServiceStack AutoQuery into custom DTO

So, I'm working with ServiceStack, and know my way around a bit with it. I've used AutoQuery and find it indispensable when calling for straight 'GET' messages. I'm having an issue though, and I have been looking at this for a couple of hours. I hope it's just something I'm overlooking.
I have a simple class set up for my AutoQuery message:
public class QueryCamera : QueryDb<db_camera>
{
}
I have an OrmLite connection that is used to retrieve db_camera entires from the database. this all works just fine. I don't want to return a model from the database though as a result, I'd like to return a DTO, which I have defined as another class. So, using the version of QueryDb, my request message is now this:
public class QueryCamera : QueryDb<db_camera, Camera>
{
}
Where the Camera class is my DTO. The call still executes, but I get no results. I have a mapper extension method ToDto() set up on the db_camera class to return a Camera instance.
Maybe I'm just used to ServiceStack making things so easy... but how do I get the AutoQuery request above to perform the mapping for my request? Is the data retrieval now a manual operation for me since I'm specifying the conversion I want? Where's the value in this type being offered then? Is it now my responsibility to query the database, then call .ToDto() on my data model records to return DTO objects?
EDIT: something else I just observed... I'm still getting the row count from the returned dataset in AutoQueryViewer, but the field names are of the data model class db_camera and not Camera.
The QueryDb<From, Into> isn't able to use your Custom DTO extension method, it's used to select a curated set of columns from the executed AutoQuery which can also be used to reference columns on joined tables.
If you want to have different names on the DTO than on your Data Model, you can use an [Alias] attribute to map back to your DB column name which will let you name your DTO Property anything you like. On the other side you can change what property the DTO property is serialized as, e.g:
[DataContract]
public class Camera
{
[DataMember(Name = "Id")] // serialized as `Id`
public camera_id { get; set; } // populated with db_camera.camera_id
[DataMember]
[Alias("model")] // populated with db_camera.model
public CameraModel { get; set; } // serialized as `CameraModel`
}

ServiceStack Deserializing Interface property null in object

I want to use ServiceStack JsonSerializer.
I am setting IncludeTypeInfo property true before I serialize my object.And my serialized string contains all type informations like "__type":".Interfacesb,....
When I want to deserialize string that time my interface property null even though I have type information in my serialized string.Is there any other configuration need when deserializing object.
I use two methods
JsonSerializer.SerializeToString and JsonSerializer.DeSerializeFromString
Example:
JsConfig.IncludeTypeInfo = true;
Public Class MyObject
{
Public string a{get;set;}
Public interface b{get;Set;}
}
First, the version 4.* is the continued developed version. 3.9 is not actively maintained by anyone.
Test on servicestack.text 4.50
Secondly i don't think this this property was made to de-serialize it back practical objects.
i did the same in 4.50 and it just doesn't deserialize:
Alternative solutions
Here you can read what to if you want the types from the json: https://stackoverflow.com/a/21603948/1275832.
When you have the type:
I use the following code as an alternative solution (note its an extension method) as a solution for run-time dynamic types (v4.50):
public static object FromJson(this string json, Type deserializeType)
{
return typeof(JsonSerializer).GetMethod("DeserializeFromString", BindingFlags.Static)
.MakeGenericMethod(deserializeType)
.Invoke(null, new[] { json });
}
and usage as: var object = (MyInterface)jsonString.FromJson(Type.GetType(AssemblyQualifiedNameString));

Enforcing type info on values of object arrays in ServiceStack

Is there a way to enforce the __type attribute when serializing object arrays as JSON? For example, I have following array:
var data = new object[]
{
"string value",
new Dictionary<string, object>
{
{"name", 1.23}
}
};
When I serialize this using
var json = JsonSerializer.SerializeToString(data);
I get following result
["string value",{"name":1.23}]
Deserializing using
var result = JsonSerializer.DeserializeFromString<object[]>(json);
// result == new object[] {"string value", "{name:1.23}"}
leads to an array containing two strings. The second one is the JSON serialized dictionary representation.
Is there a way to enforce the __type attribute to allow ServiceStack to deserialize to the right type? The array can contain almost anything JSON serializable.
This is an internal API with ServiceStack on both sides. The possible types are not known at compile time.

ServiceStack - Request Binding JSON encoded parameter

I have an existing application that sends a Request with a parameter named 'filters'. The 'filters' parameter contains a string that is JSON encoded. Example:
[{"dataIndex":"fieldName", "value":"fieldValue"}, {"dataIndex":"field2", "value":"value2"}].
Using ServiceStack, I would like to bind this as a property on a C# object (class Grid). Is there a preferred method to handle this? Here are the options I can think of. I don't think either 'feel' correct.
Option 1:
I do have a 'ServiceModel' project and this would create a dependency on it which I don't really like.
In AppHost.Configure() method add
RequestBinders[typeof(Grid)] => httpReq => {
return new Grid() {
Filters = new ServiceStack.Text.JsonSerializer<IList<Filter>>().DeserializeFromString(httpReq.QueryString["filters"])
}
}
Option 2:
Seems kind of 'hacky'
public class Grid
{
private string _filters;
public dynamic Filters {
get
{
ServiceStack.Text.JsonSerializer<IList<Filter().DeserializeFromString(_filters);
}
set
{
_filters = value;
}
}
}
You can send Complex objects in ServiceStack using the JSV Format.
If you want to send JSON via the QueryString you can access it from inside your Service of Request filters with something like:
public object Any(Request req) {
var filters = base.Request.QueryString["Filters"].FromJson<List<Filter>>();
}
Note: Interfaces on DTOs are bad practice.

ektorp / CouchDB mix HashMap and Annotations

In jcouchdb I used to extend BaseDocument and then, in a transparent manner, mix Annotations and not declared fields.
Example:
import org.jcouchdb.document.BaseDocument;
public class SiteDocument extends BaseDocument {
private String site;
#org.svenson.JSONProperty(value = "site", ignoreIfNull = true)
public String getSite() {
return site;
}
public void setSite(String name) {
site = name;
}
}
and then use it:
// Create a SiteDocument
SiteDocument site2 = new SiteDocument();
site2.setProperty("site", "http://www.starckoverflow.com/index.html");
// Set value using setSite
site2.setSite("www.stackoverflow.com");
// and using setProperty
site2.setProperty("description", "Questions & Answers");
db.createOrUpdateDocument(site2);
Where I use both a document field (site) that is defined via annotation and a property field (description) not defined, both get serialized when I save document.
This is convenient for me since I can work with semi-structured documents.
When I try to do the same with Ektorp I have documents using annotations and Documents using HashMap BUT I couldn't find an easy way of getting the mix of both (I've tried using my own serializers but this seems to much work for something that I get for free in jcouchdb). Also tried to annotate a HashMap field but then is serialized as an object and I get the fields automatically saved BUT inside an object with the name of the HashMap field.
Is it possible to do (easily/for free) using Ektorp?
It is definitely possible. You have two options:
Base your class on org.ektorp.support.OpenCouchDbDocument
Annotate the you class with #JsonAnySetter and #JsonAnyGetter. Red more here: http://wiki.fasterxml.com/JacksonFeatureAnyGetter

Resources