5

I have the following DTO:

public class MyDTO
{
    public int Id { get; set; }

    public String Info { get; set; }
}

The Info element contains some serialized JSON object which can be of multiple different types. In my service function, I return this DTO using return x.ConvertTo<MyDTO>()

My problem is, that, since ServiceStack is not aware that Info holds a JSON, the special characters (quotation marks) of Info are escaped.

So I get

{"Id":15,"Info":"[\"Test1\",\"Test2\",\"Test3\"]"}

from the service, but what I would like to get is actually

{"Id":15,"Info":["Test1","Test2","Test3"]}

Is there some way, to tell ServiceStack that Info holds JSON data and thus prevent it from escaping the string and instead inserting the JSON value directly into the response?

P.S.: My question is not a duplicate of that question, which is concerned with forcing the default DTO encoding of a service to JSON, while my problem deals with how the JSON encoding happens for certain types.

mat
  • 1,645
  • 15
  • 36
  • Possible duplicate of [ServiceStack default format](https://stackoverflow.com/questions/10317225/servicestack-default-format) – Pranav Patel May 30 '17 at 09:30
  • @PranavPatel My question is not a duplicate of that question. It is concerned with forcing the default DTO encoding of a service to JSON, while my problem deals with how the JSON encoding happens for certain types. – mat May 30 '17 at 09:47
  • Maybe this helps? ► https://stackoverflow.com/questions/4729811/json-net-unexpected-characters-when-serializing-my-entities-entity-framew – Nope May 30 '17 at 10:03
  • @Fran The default behaviour of the serialization process is understandable, you do no want just any quotation marks in a string to mess up your JSON types. But in this case, since I know my string is a valid JSON (since I encoded it myself when storing it in a database), I would like to integrate it seamlessly, into the JSON encoded return DTO so that the client can use it right away without having to do any derserialization duties. – mat May 30 '17 at 10:08
  • 1
    @mat what is expected is not valid JSON. This is not valid JSON `{"Id":15,"Info":"{["Test1","Test2","Test3"]}"}`. What you actually get is correct as Info is a string so thus it is escaped. – Nkosi May 30 '17 at 10:13
  • @Nkosi You are correct, thank you. I updated my question and fixed the examples. – mat May 30 '17 at 11:35
  • @mat in the snippet `x.ConvertTo()`, is `x` a JSON string or an other object type? – Nkosi May 30 '17 at 12:07
  • `x` is my database object from OrmLite, which has the same fields (*int Id* and *string Info*) as MyDTO, plus some extra fields. – mat May 30 '17 at 12:11
  • @mat, I was thinking of something with generics like `public class MyDTO { public int Id { get; set; } public T Info { get; set; } }` but still working out how to do the conversion based on the source. – Nkosi May 30 '17 at 12:13
  • 1
    @Nkosi That won't work. Info can basically be encoded from any type. The decoding and interpretation of the values are done solely in the frontend. – mat May 30 '17 at 12:15

2 Answers2

2

using composition you can interpret the Info property of MyDTO

public class MyDTO<T> : MyDTO {

    public MyDTO(MyDTO dto) {
        this.Id = dto.Id;
        this.Info = JsonConvert.DeserializeObject<T>(dto.Info);
    }

    public new T Info { get; set; }
}

that way the JSON value in Info can be normalized before returning it to be serialized.

For example

var dto = x.ConvertTo<MyDTO>();
return new MyDTO<dynamic>(dto);

If dto.Info was a JSON array of strings would allow the array to be serialized as desired in the OP

var dto = new MyDTO {
    Id = 15,
    Info = "[\"Test1\",\"Test2\",\"Test3\"]"
}

would produce

{"Id":15,"Info":"[\"Test1\",\"Test2\",\"Test3\"]"}

where as

new MyDTO<dynamic>(dto);

would produce

{"Id":15,"Info":["Test1","Test2","Test3"]}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 1
    That doesn't work. ServiceStack can't return a dynamic interface from a service. – mat May 30 '17 at 13:43
  • 1
    Play around with other return types. This was just meant as an example that it is possible. I ran this through some unit tests and it serialized just like I said. – Nkosi May 30 '17 at 13:52
  • @mat Returning JSON in a string is always escaped as a JSON string, irrespective if it contains valid JSON or not. This answer to deserialize it into a generic type is the right one although you shouldn't put any logic in DTOs, (I prefer to use an extension method instead). The other option is to return an `object` although that [should be heavily discouraged](https://stackoverflow.com/a/10759250/85785). – mythz May 30 '17 at 17:32
2

My co-workers and I faced a similar problem, and with the help of Demis Bellot we were able to arrive at a solution that, if translated to the code in the OP, would look like this:

public class MyDTO
{
    public int Id { get; set; }

    public Dictionary<string, object> Info { get; set; }
}

When we populate the DTO, we use JSON.parse like this:

var json = (Dictionary<string, object>)JSON.parse(rawJsonString);
return new MyDTO
{
    Id = 42,
    Info = json
};

Our raw JSON string was a JSON object. In the OP, it looks like the raw JSON string may, instead, be an array, in which case it looks as though the appropriate property type might be List<object>.

JSON.parse can be found in ServiceStack.Common.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736