Service Stack is double escaping?

We use nested JsonObjects in our DTOs in order to have a dynamic object, Sometimes customers post data like “http://myuri.de” but in this case we run into an issue that:

var o = new JsonObject();
o.Add("url", @"http:\/\/myuri.de");
Console.WriteLine(o.ToJson());

we will get (escaped backslash):

{"url":"http:\\/\\/myuri.de"}

instead of:

{"url":"http:\/\/myuri.de"} 
or better
{"url":"http://myuri.de"} like with Console.WriteLine(o["url"])

is there a way to avoid this issue?

It’s being escaped correctly as it needs to include the back slashes contained in the string:

@"http:\/\/myuri.de"

Is the same as:

"http:\\/\\/myuri.de"

yes sounds logical, but if we use ExpandoObject instead of JsonObject we doesn’t run in this issue:

var b = @"{""url"":""http:\/\/myuri.de""}";
var jObj = ServiceStack.Text.JsonSerializer.DeserializeFromString<ExpandoObject>(b);
Console.WriteLine(ServiceStack.Text.JsonSerializer.SerializeToString(jObj));

output is

{"url":"http://myuri.de"}

What issue, if "http:\/\/myuri.de" unescapes to c# string http://myuri.de, if you don’t want the back slashes don’t add them, they’re already not needed.

… normally I expect that if someone sends data with e.g. “http://myuri.de” to our REST API the minimum is that after deserialization and working with the deserialized data and then serialize it back to Json… I at least expect to get the same data back => “http://myuri.de” ?

I think you mean that the contents in JsonObject are stored unescaped, that’s just how it works, you’d use the APIs to retrieve escaped values from it, e.g:

var url = jsonObj.Get("url");

Otherwise you can just deserialize it into a string dictionary:

var strDict = b.FromJson<Dictionary<string,string>>();

ok b.FromJson<Dictionary<string,string>>(); returns a Json string without backslash with e.g.

var jObjDict = b.FromJson<Dictionary<string, string>>();
Console.WriteLine(ServiceStack.Text.JsonSerializer.SerializeToString(jObjDict));

but we have DTOs with nested JsonObject so wtih

Console.WriteLine(ServiceStack.Text.JsonSerializer.SerializeToString(MyDTO.JsonObject.ToDictionary()));

we run into the same issue…

…the nested JsonObject in our DTOs are like pass through parameter so if a consumer saves an object via our REST API, he would like to get the same thing back again …

REST API POST "http:\/\/myuri.de" 
=>  REST API GET "http:\/\/myuri.de" and not REST API GET "http:\\/\\/myuri.de"

We have to consider that slashes are passed escaped to our API, since escaping slashes is vailid (but optional). I think the underlying problem is that consecutive (de-)serializaition via JsonObject

var s0 = @"{""escapeTest"":"" / \/ \""test\"" \\ ""}";
Console.WriteLine(s0);
var s1 = JsonSerializer.SerializeToString(JsonSerializer.DeserializeFromString<JsonObject>(s0));
Console.WriteLine(s1);
var s2 = JsonSerializer.SerializeToString(JsonSerializer.DeserializeFromString<JsonObject>(s1));
Console.WriteLine(s2);
var s3 = JsonSerializer.SerializeToString(JsonSerializer.DeserializeFromString<JsonObject>(s2));
Console.WriteLine(s3);

results in backslashes multiplied by a power of 2:

{"escapeTest":" / \/ \"test\" \\ "}
{"escapeTest":" / \\/ \\\"test\\\" \\\\ "}
{"escapeTest":" / \\\\/ \\\\\\\"test\\\\\\\" \\\\\\\\ "}
{"escapeTest":" / \\\\\\\\/ \\\\\\\\\\\\\\\"test\\\\\\\\\\\\\\\" \\\\\\\\\\\\\\\\ "}

Right you don’t want to be using JsonObject for serialization, it’s not a dynamic object, it’s a wrapper around Dictionary<string,string> that holds unescaped string values that you can choose to access both the escaped an unescaped values out with obj.Get() and obj.GetUnescaped().

You should use Dictionary<string,string> where it’s appropriate. ServiceStack also supports object types which is more “dynamic” since it will return the actual .NET Data Structure instead of coercing everything into a Dictionary<string,string> however using dynamic object is poor API design that ideally should not be your Service Contracts.

but when using object instead of JsonObject:

var b = @"{""url"":""http:\/\/myuri.de""}";
var jObj = ServiceStack.Text.JsonSerializer.DeserializeFromString<Object>(b);
Console.WriteLine(ServiceStack.Text.JsonSerializer.SerializeToString(jObj));

we get:

"{url:http://myuri.de}"

instead

{"url":"http:\/\/myuri.de"}  or {"url":"http://myuri.de"}

It works differently in ServiceStack Services because it replaces the implementation of ServiceStack.Text object handling with JS Utils which is in ServiceStack.Common.

You can test it with:

var obj = JSON.parse(@"{""url"":""http:\/\/myuri.de""}") as Dictionary<string,object>;

But you’ll still get unescaped string, i.e:

[url, http://myuri.de]

To hookup the different serialization behavior in ServiceStack.Text:

JS.Configure(); // replace object handling

// JsonSerializer.

JS.UnConfigure(); // restore object handling

ok thanks now it looks better already

Hi, I’ve one more question is there any way to get an Exception instead of 0 after deserialize to Dictionary<string,object> in the following example (number with 2 decimal points ):

JS.Configure(); 
var obj = JSON.parse(@"{""test"":23.34.3333}") as Dictionary<string,object>;

Should now throw on invalid numbers from this commit.

This change is available from the latest v5.5.1 that’s now available on MyGet.

Thank you very much… good job