2013-09-10 4 views
4

Допустим, что есть простой интерфейс:Создать список типов интерфейсов из массива JavaScript

public interface ISerialize 
    { 
     string FirstProp { get; set; } 
     string SecondProp { get; set; } 

    } 

, который реализуется с помощью классов:

public class Class1 : ISerialize 
    { 
     public string FirstProp { get; set; } 
     public string SecondProp { get; set; } 
     public string ThirdProp { get; set; } 
    } 
    public class Class2 : ISerialize 
    { 
     public string FirstProp { get; set; } 
     public string SecondProp { get; set; } 
     public string FourthProp { get; set; } 
    } 

в данный момент (который не означали долго -термическая стабильность) У меня есть веб-страница, которая выглядит так: http://jsfiddle.net/SBbPT/ где каждое текстовое поле соответствует свойству в объекте Class1 или Class2, а ссылка Add to batch добавляет объект в массив JavaScript, а кнопка Submit batch отправляет J SON для веб-службы строкового объекта. На данный момент следующего JS определяет, какой тип, Class1 или Class2 создан:

$(document).ready(function() 
     { 
      var iSerialize = []; 
      $('#btnSubmit').click(function() 
      { 
       //creates Class1 object if ThirdProp is present 
       if ($('#txt3').val().length > 0) 
       { 
        var class1 = { FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), ThirdProp: $('#txt3').val() } 
        iSerialize.push(class1); 
       } 
       else 
       { 
        var class2 = { FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), FourthProp: $('#txt4').val() }; 
        iSerialize.push(class2); 
       } 
       $('input').val(''); 
      }); 
      $('#btnSubmitBatch').click(function() 
      { 
       var data = "{jsonString:'" + JSON.stringify(iSerialize) + "'}"; 
       console.log(data); 
       $.ajax(
       { 
        type: "POST", 
        url: "default.aspx/DataList", 
        contentType: "application/json", 
        dataType: "json", 
        data: data, 
        success: function (data) 
        { 
         console.log('the post was successful'); 
         console.log(data.d); 
        }, 
        error: function (xhr) 
        { 
         console.log(xhr.status); 
        } 
       }); 
      }); 

     }); 

В настоящее время, если пользователь оставляет текстовое поле FourthProp пустым, объект Class1 должен быть создан и, если пользователь оставить текст ThirdProp пустое поле, должен быть создан объект Class2. Мой текущий метод веб-службы выглядит следующим образом:

[WebMethod] 
     public string DataList(string jsonString) 
     { 
      var jss = new JavaScriptSerializer(); 
      List<ISerialize> list = jss.Deserialize<List<ISerialize>>(jsonString); 
      //some members might have different properties 
      //how to create a factory to create an instance based on the objects properties? 
      return list[0].FirstProp; 
     } 

В своем нынешнем состоянии я получаю сообщение об ошибке: No parameterless constructor defined for type of DeserializeListOfInterfaceTypes.ISerialize. Этого можно избежать, и программа будет работать, делая List<ISerialize> список одного из конкретных типов. Таким образом, в этом случае наличие свойства ThirdProp или FourthProp определяет, должен ли объект быть Class1 или Class2, соответственно. Как я могу использовать свойства объекта JavaScript для определения того, какой объект C# создать?

ответ

1

Если вы хотите продолжать использовать JavaScriptSerializer, то вы можете написать пользовательские JavaScriptTypeResolver:

public class CustomTypeResolver : JavaScriptTypeResolver 
{ 
    public override Type ResolveType(string id) 
    { 
     return id == "class1" ? typeof(Class1) : typeof(Class2); 
    } 

    public override string ResolveTypeId(Type type) 
    { 
     return type == typeof(Class1) ? "class1" : "class2"; 
    } 
} 

, а затем построить свой сериалайзер следующим образом:

var jss = new JavaScriptSerializer(new CustomTypeResolver()); 

Наконец, вам нужно добавить этот тип информацию на поле __type в вашем JavaScript-коде:

$('#btnSubmit').click(function() 
{ 
    //creates Class1 object if ThirdProp is present 
    if ($('#txt3').val().length > 0) 
    { 
     var class1 = { __type: "class1", FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), ThirdProp: $('#txt3').val() } 
     iSerialize.push(class1); 
    } 
    else 
    { 
     var class2 = { __type: "class2", FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), FourthProp: $('#txt4').val() }; 
     iSerialize.push(class2); 
    } 
    $('input').val(''); 
}); 

Однако, если вы не хотите добавлять какие-либо другие объекты к объектам, я бы согласился с jods's answer и использовать Newtonsoft's JSON.NET вместо JavaScriptSerializer.

2

Проблема заключается в том, что де-сериализатор должен создавать классы для заполнения списка, а не интерфейсов. Но он не знает, какой класс.

Итак, ваш вопрос сводится к: как намекнуть десериализатор, какой класс создать для каждого объекта JSON?

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

Предполагая, что вы используете JSON.net Newtonsoft для сериализации (если вы этого не сделаете). Именно этот вопрос был задан и ответил здесь:

How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?

Это в основном сводится к тому, перекрытый JsonConverter.

2

Вы можете решить эту проблему двумя способами:

  1. Написать пользовательский сериализатор, который будет знать ваши конкретные требования и иметь логику о том, что именно тип для создания экземпляра.
  2. Изменение объектной модели вашего сервера в зависимости от семантики вашего веб-приложения.

Если вы берете подход 1, а не JavaScriptSerializer, вы должны реализовать пользовательский.Вот начальная точка в том, как это сделать: http://msdn.microsoft.com/en-us/library/ty01x675.aspx

Если вы решите изменить свою объектную модель, вам необходимо убедиться, что она работает со значением по умолчанию JavaScriptSerializer, который требует создания конкретных типов. Теперь, в зависимости от семантики, Class1 и Class2 могут быть похожи друг на друга (наследование). В этом случае вы можете извлечь свойства, которые в настоящее время включены в интерфейс, в базовый класс и сделать все, что необходимо переопределить виртуальным. Затем Class1 и Class2 переопределяют свойства, но они все равно будут определены в базовом классе, и сериализатор сможет создать экземпляр этого класса. Вместо интерфейса вы должны указать базовый класс как тип сериализатора. Если Class1 и Class2 не имеют никакого поведения, вы все равно можете создать базовый класс для обслуживания только для сериализации. Конечно, это зависит от семантики, о которой я ничего не знаю.

1

Маршрут я бы лично выбрать может быть следующее:

Создать класс, который может провести любой данный вход для Class1 плюс Class2, такие как

public class ClassInput 
{ 
    public string FirstProp { get; set; } 
    public string SecondProp { get; set; } 
    public string ThirdProp { get; set; } 
    public string FourthProp { get; set; } 
} 

Используйте этот класс в качестве аргумента для вашего веб-метод.

Затем определите на стороне сервера, какой класс должен быть создан.

Имейте метод для каждого из Class1 и Class2, который создает этот класс и отображает каждое свойство с ClassInput.

public Class1 CreateClass1(ClassInput input) 
{ 
    Class1 output = new Class1(); 
    output.FirstProp = input.FirstProp; 
    output.SecondProp = input.SecondProp; 
    output.ThirdProp = input.ThirdProp; 
    return output; 
} 

public Class2 CreateClass2(ClassInput input) 
{ 
    Class2 output = new Class1(); 
    output.FirstProp = input.FirstProp; 
    output.SecondProp = input.SecondProp; 
    output.FourthProp = input.FourthProp; 
    return output; 
} 

Использование AutoMapper может сохранить вам некоторый код здесь, если у вас есть много классов/свойств.

Смежные вопросы