JSON convert JsonResult to javascript string without encoding quotations

12,527

Solution 1

In ASP.Net 5 there is a JsonHelper directly available in the razor views via @Json.Serialize, so you can write something like this in your razor view:

var foo = @Json.Serialize(model);

This is very similar to manually doing:

var foo = @Html.Raw(JsonConvert.SerializeObject(model))

The problem in both cases is that JSON.Net by default serializes but does not XSS-sanitizes the output. This means the contents of model are not escaped, leaving your code open for XSS attacks if model depends on user input. For example:

@{
    var model = new
    {
        prop1 = 1,
        prop2 = "</script><script>alert('o hai');</script>",
        prop3 = false
    };
}
var foo = @Json.Serialize(model);

You could use the Json.Serialize overload that lets you specify JsonSerializerSettings, so you can set StringEscapeHandling as EscapeHtml:

HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped.

var foo = @Json.Serialize(model, new JsonSerializerSettings { StringEscapeHandling = StringEscapeHandling.EscapeHtml });

PS. I have raised an issue on github to include default html escaping in the JsonHelper.

Approaches used in previous versions

If you search about this same question on stack overflow, you will see there used to be a Json.Encode helper in earlier versions of MVC, so you could serialize like this, manually performing the json serialization. This seems replaced by Json.Serialize which is not safe by default.

There also used to be a helper HttpUtility.JavaScriptStringEncode that could be used to manually encode json literals. This seems to be there in ASP.Net 5 as IJavaScriptStringEncoder. The only problem is that when combined with Newtonsoft.Json (The default json formatter in ASP.Net 5), it will encode too much for a valid json literal (like the double quotes in property names). However it will produce a valid string that can be parsed on the browser with JSON.parse (So probably this approach should work too):

@inject Microsoft.Extensions.WebEncoders.IJavaScriptStringEncoder encoder;
...
var foo = JSON.parse("@encoder.JavaScriptStringEncode(JsonConvert.SerializeObject(model))");

Be also aware that by looking at the newest source code, the IJavaScriptStringEncoder will be replaced by System.Text.Encodings.Web.JavaScriptEncoder in RC2.

Solution 2

You could use Newtonsoft

"Newtonsoft.Json": "6.0.8",

and then on your JsonResult Object in your Controller

using Newtonsoft.Json;

and

var jsonResult = Json(myObject);

string jsonString= JsonConvert.SerializeObject(jsonResult.Value);

Solution 3

String - JavaScriptSerializer: (Can't use in ASP.NET Core)

var json = new JavaScriptSerializer().Serialize(jsonResult.Data);

Object - Newtonsoft.Json:

var json = JsonConvert.SerializeObject(jsonResult.Data, Formatting.Indented, new KeysJsonConverter(typeof(OBJECT)));

KeysJsonConverter

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace TestApp.Helpers
{
    // Creates a custom JsonConverter that overrides serialization to add a keys property.

    public class KeysJsonConverter : JsonConverter
    {
        private readonly Type[] _types;

        public KeysJsonConverter(params Type[] types)
        {
            _types = types;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            JToken t = JToken.FromObject(value);

            if (t.Type != JTokenType.Object)
            {
                t.WriteTo(writer);
            }
            else
            {
                JObject o = (JObject)t;
                IList<string> propertyNames = o.Properties().Select(p => p.Name).ToList();

                o.AddFirst(new JProperty("Keys", new JArray(propertyNames)));

                o.WriteTo(writer);
            }
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
        }

        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanConvert(Type objectType)
        {
            return _types.Any(t => t == objectType);
        }
    }
}

.cshtml

 var model = @Html.Raw(Json.Encode(Model));

Not sure where i got KeysJsonConverter from but i have used it in a few projects i have built and it works very well, credit to whoever wrote it originally.

Share:
12,527
greay
Author by

greay

Developer aiming to slowly but surely get better.

Updated on June 17, 2022

Comments

  • greay
    greay almost 2 years

    I need to convert a Microsoft.AspNet.Mvc.Controller.JsonResult to a string using ASP.NET Core

    Here is my code in ASP.NET MVC

    public async Task<string> GetJsonData()
    {
        //...
        var result = GetMyComplexObject();
    
        //using Newtonsoft.Json.JsonConvert method
        var jsonString = JsonConvert.SerializeObject(Json(result));
        return jsonString;
    }
    
    public async Task<IActionResult> Index()
    {
        var myViewModel= new MyViewModel();
        myViewModel.JsonString = GetJsonData();
        return View(myViewModel);
    }
    
    public class MyViewModel
    {
        //...
        public string JsonString { get; set; }
    }
    

    and in my cshtml

    var dataSet = {@(Model.JsonString))};
    

    However in the generated source instead of the fields being in quotations, they are html encoded e.g. &quot;field1&quot;

    Instead of running string replaces is there not a neater way to convert a JsonResult to string

  • greay
    greay almost 8 years
    JavaScriptSerializer from what I know cannot be used in ASP.NET Core as it is part of the System namespace
  • David
    David almost 8 years
    He is serializing a object anyway using Newtonsoft.Json.JsonConvert method, which i have shown above :) you are right you can't use JavaScriptSerializer in ASP.NET Core. But you can use Newtonsoft.Json as its a dependency of Microsoft.AspNet.Mvc.ModelBinding.
  • Adam H
    Adam H over 7 years
    This is what i was looking for @Json.Serialize(Model);, great answer as well. Thanks!!
  • Ben Gribaudo
    Ben Gribaudo over 7 years
    Thanks for this great answer! Using the ASP.Net 5 version with JsonSerializerSettings may require the developer to add @using Newtonsoft.Json to the view in order to bring JsonSerializerSettings's containing namespace into scope.