2016-12-08 2 views
0

давно я установил стандарт кодирования для моего приложения, что все действия, возвращающихся JSON бы их результаты помещаются в объект верхнего уровня обертки:Настройка JSON сериализация в выходе действий MVC

var result = { 
    success: false, 
    message: 'Something went wrong', 
    data: {} // or [] 
} 

Это хорошо работал , и обеспечил мне хорошее счастье стандартизации кода.

Сегодня, однако, я понял, что мой серверный код предполагает, что он всегда получает полную сериализацию того, что возвращается. Теперь я хотел бы сериализовать одного из этих парней, где полезная нагрузка «данных» уже является хорошо сформированной строкой JSON.

Это общая схема, которая работает:

bool success = false; 
string message = "Something went wrong"; 
object jsonData = "[{\"id\":\"0\",\"value\":\"1234\"}]"; // Broken 

dynamic finalData = new { success = success, message = message, data = jsonData }; 

JsonResult output = new JsonResult 
{ 
    Data = finalData, 
    JsonRequestBehavior = JsonRequestBehavior.AllowGet, 
    MaxJsonLength = int.MaxValue 
}; 
return output; 

Где ломает то, что элемент «данные» будут получены в виде строки, когда он попадает в браузере, а не в качестве надлежащего JSON объект (или массив в приведенном выше примере) должен быть.

Есть ли способ украсить свойство атрибутом, который говорит «сериализовать как необработанный», или я в области написания пользовательского сериализатора JSON, чтобы сделать эту работу?

+1

Вы должны использовать 'json.parse'. Прочтите полную информацию здесь https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse –

+1

Ну, технически вы нарушаете свой собственный контракт и стандарт. Раньше вы ожидали * объекта *, теперь вы передаете ему строку. Мне кажется, что это плохой путь, но вы можете * написать 'data = JsonSerializer.Deserialize (jsonData)', если нет возможности избежать этой строки. Однако обратите внимание, что вы будете сериализовать его только для последующего десериализации. – Rob

+0

json.parse находится в конце браузера.Я пытаюсь манипулировать сериализацией на стороне сервера. Роб, ты точно входишь в проблему. Я не хочу ее разбирать, просто чтобы сразу же пересмотреть ее (и на самом деле просто положить тонкую обертку вокруг того, что было уже отлично JSON). Я думаю, что я собрал решение, и опубликую это завтра. Спасибо вам обоим! – Eric

ответ

0

Вот что я закончил с ....

// Wrap "String" in a container class 
public class JsonStringWrapper 
{ 
    // Hey Microsoft - This is where it would be nice if "String" wasn't marked "sealed" 
    public string theString { get; set; } 
    public JsonStringWrapper() { } 
    public JsonStringWrapper(string stringToWrap) { theString = stringToWrap; } 
} 

// Custom JsonConverter that will just dump the raw string into 
// the serialization process. Loosely based on: 
// http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm 
public class JsonStringWrapperConverter : JsonConverter 
{ 
    private readonly Type _type = typeof(JsonStringWrapper); 

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

     if (t.Type != JTokenType.Object) 
     { 
      t.WriteTo(writer); 
     } 
     else 
     { 
      string rawValue = ((JsonStringWrapper)value).theString; 
      writer.WriteRawValue((rawValue == null) ? "null" : rawValue); 
     } 
    } 

    public override bool CanWrite 
    { 
     get { return true; } 
    } 

    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 _type == objectType; 
    } 
} 

// Custom JsonResult that will use the converter above, largely based on: 
// http://stackoverflow.com/questions/17244774/proper-json-serialization-in-mvc-4 
public class PreSerializedJsonResult : JsonResult 
{ 
    private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings 
    { 
     Converters = new List<JsonConverter> { new JsonStringWrapperConverter() } 
    }; 

    public override void ExecuteResult(ControllerContext context) 
    { 
     if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && 
      string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
     { 
      throw new InvalidOperationException("GET request not allowed"); 
     } 

     var response = context.HttpContext.Response; 

     response.ContentType = !string.IsNullOrEmpty(this.ContentType) ? this.ContentType : "application/json"; 

     if (this.ContentEncoding != null) 
     { 
      response.ContentEncoding = this.ContentEncoding; 
     } 

     if (this.Data == null) 
     { 
      return; 
     } 

     response.Write(JsonConvert.SerializeObject(this.Data, Settings)); 
    } 
} 

// My base controller method that overrides Json()... 
protected JsonResult Json(string message, object data) 
{ 
    PreSerializedJsonResult output = new PreSerializedJsonResult(); 

    object finalData = (data is string && (new char[] { '[', '{' }.Contains(((string)data).First()))) 
     ? new JsonStringWrapper(data as string) 
     : data; 

    output.Data = new 
    { 
     success = string.IsNullOrEmpty(message), 
     message = message, 
     data = finalData 
    }; 
    output.JsonRequestBehavior = JsonRequestBehavior.AllowGet; 
    output.MaxJsonLength = int.MaxValue; 
    return output; 
} 

// Aaaand finally, here's how it might get called from an Action method: 
... 
return Json("This was a failure", null); 
... 
return Json(null, yourJsonStringVariableHere); 

С этим, я не делаю каких-либо JSon синтаксический на сервере. Моя строка выводится из базы данных и идет прямо к клиенту, не затрагивая MVC.

EDIT: Обновленная версия теперь также поддерживает сериализацию объектов, которые имеют индивидуальные свойства где-то в своей иерархии, которые имеют тип JsonStringWrapper. Это полезно в моем сценарии для поддержки «гибридной» модели. Если объект A имеет свойство B, которое является одной из моих предварительно испеченных строк JSON, код выше будет правильно обрабатывать это.

1

Вы сериализуете его дважды (выход jsonData +). Вы не можете этого сделать и ожидаете только десериализации его один раз (вывод).

Вы можете установить объект «данные» в своей динамике как реальный объект C#, который будет работать. Или вы можете повторно назвать вашу собственность «jsonData»:

dynamic finalData = new { success = success, message = message, jsonData = jsonData }; 

... так что это отражает то, что вы действительно делаете :).

+0

Спасибо, jvenema, я ценю ответ. Моя проблема в том, что на самом деле это не объект C#. Это происходит прямо из SQL как строки JSON. У меня даже нет типа где-нибудь в моем среднем уровне, который приравнивается к нему. Я просто пытаюсь, чтобы веб-сервер был сквозным. Это говорит - я думаю, что я собрал решение с помощью пользовательского сериализатора, основанного на нескольких других потоках. Если это сработает, я опубликую то, что я придумал завтра. – Eric

0

Вы можете сделать это, сформировав пакет JSON самостоятельно, используя класс JsonWriter от Newtonsoft. Это будет выглядеть примерно так:

using(var textWriter = new StringWriter()) 
using(var jsonWriter = new JsonTextWriter(textWriter)) 
{ 
    jsonWriter.WriteStartObject(); 

    jsonWriter.WritePropertyName("success"); 
    jsonWriter.WriteValue(success); 

    jsonWriter.WritePropertyName("message"); 
    jsonWriter.WriteValue(message); 

    jsonWriter.WritePropertyName("data"); 
    jsonWriter.WriteRaw(jsonData); 

    jsonWriter.WriteEndObject(); 

    var result = new ContentResult(); 
    result.Content = textWriter.ToString(); 
    result.ContentType = "application/json"; 
    return result; 
} 
0

Я думаю, что вам просто нужно сериализовать строку возвращается из таблицы SQL в объект, используя JSON сериализатор, как NewtonSoft.

bool success = false; 
string message = "Something went wrong"; 
string rawData = "[{\"id\":\"0\",\"value\":\"1234\"}]"; // Broken 
object jsonData = JsonConvert.DeserializeObject<dynamic>(rawData); 

dynamic finalData = new { success = success, message = message, data = jsonData }; 

JsonResult output = new JsonResult 
{ 
    Data = finalData, 
    JsonRequestBehavior = JsonRequestBehavior.AllowGet, 
    MaxJsonLength = int.MaxValue 
}; 
return output; 
Смежные вопросы