Десериализация внутренней структуры в строку с помощью JavaScriptSerializer в .NET

Я использую JavaScriptSerializer в .NET для десериализации структур, которые могут быть вашей стандартной плоской парой ключ/значение, например:

{
    "Value": 3.14,
    "Type": "Real"
}

Или более сложный, с таким внутренним иерархическим объектом-значением:

{
    "Value": { "something": "test" },
    "Type": "JsonData"
}

У меня проблема в том, что в случае "Type": "JsonData" объект может быть произвольным. Я не могу сделать никаких предположений относительно того, какой будет его структура.

Однако «верхние слои» моего приложения будут знать, что делать с этими данными.

Поэтому мне нужно найти способ сохранить поле Value в виде строки, независимо от того, содержит ли оно целое число, строку или полную структуру JSON. По сути, я не хочу, чтобы JavaScriptSerializer там что-то анализировал. Я хотел бы иметь возможность иметь такой класс:

public class JsonObject
{
    public string Value { get; set; }
    public string Type { get; set; }
}

С Value, содержащим необработанное ванильное значение, которое было в моем JSON.

Есть ли способ запретить .NET анализировать часть моего JSON?

Пожалуйста, не предлагайте использовать другую библиотеку, такую ​​как JSON.NET. Я знаю, что он, вероятно, сможет это сделать, но это для проекта, который будет распространяться как библиотека. Вот почему я предпочел бы, чтобы моя библиотека зависела только от стандартной платформы .NET.


person Florian Segginger    schedule 04.09.2015    source источник
comment
Я должен сказать, что JSON.Net — лучшая из доступных json библиотек синтаксического анализа.   -  person Amit Kumar Ghosh    schedule 04.09.2015
comment
Я знаю, и я использовал его много раз во многих других проектах :) Однако это совершенно излишне для моего проекта, и мне действительно нужно использовать то, что предоставляет стандартная среда выполнения .NET, и ничего больше!   -  person Florian Segginger    schedule 04.09.2015
comment
ASP.NET MVC также использует Json.Net, это должно соответствовать требованиям. Требование библиотеки не означает, что вы не можете использовать Json.NET. Это не излишество, это проще, удобнее и совместимее, чем устаревший JavaScriptSerializer.   -  person Panagiotis Kanavos    schedule 04.09.2015
comment
Кроме того, в чем проблема? Разве вы не можете просто десериализовать тип, который вы предоставили? Что вы пробовали и в чем проблема?   -  person Panagiotis Kanavos    schedule 04.09.2015
comment
@PanagiotisKanavos У меня проблема в том, что в случае Type: JsonData объект может быть произвольным. Я не могу сделать никаких предположений относительно того, какой будет его структура.   -  person Nikhil Vartak    schedule 04.09.2015
comment
@NikhilV, ОП, сказал, что хотел бы использовать класс, где Type - это строка - почему бы тогда не использовать такой класс? Десериализация‹T› принимает цель тип   -  person Panagiotis Kanavos    schedule 04.09.2015
comment
@PanagiotisKanavos Что вы предлагаете тогда указать в качестве типа в Deserialize<T>?   -  person Florian Segginger    schedule 04.09.2015


Ответы (2)


Нет атрибута, указывающего JavaScriptSerializer не анализировать конкретное свойство JSON (кроме полного его игнорирования, а это не то, что вам нужно). Однако вы можете создать JavaScriptConverter, который эффективно повторно сериализует рассматриваемое свойство, так что вы все равно получите желаемый результат: все значения заканчиваются строками.

Вот как может выглядеть преобразователь:

public class JsonObjectConverter : JavaScriptConverter
{
    public override IEnumerable<Type> SupportedTypes
    {
        get { return new List<Type> { typeof(JsonObject) }; }
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        JsonObject obj = new JsonObject();
        obj.Type = (string)dictionary["Type"];
        obj.Value = serializer.Serialize(dictionary["Value"]);
        return obj;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Чтобы использовать конвертер, вы должны зарегистрировать его в JavaScriptSerializer методом RegisterConverters. Вот демо:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        [
            {
                ""Value"" : ""foo bar"",
                ""Type"" : ""String""
            },
            {
                ""Value"" : 3.14,
                ""Type"" : ""Real""
            },
            {
                ""Value"" : 
                {
                    ""Something"" : ""Test"",
                    ""Items"" : [""A"", ""B"", ""C""]
                },
                ""Type"" : ""JsonData""
            },
            {
                ""Value"" : 42,
                ""Type"" : ""Integer""
            }
        ]";

        JavaScriptSerializer jss = new JavaScriptSerializer();
        jss.RegisterConverters(new List<JavaScriptConverter> { new JsonObjectConverter() });

        List<JsonObject> list = jss.Deserialize<List<JsonObject>>(json);
        foreach (JsonObject item in list)
        {
            Console.WriteLine(string.Format("({0}) {1}", item.Type, item.Value));
        }
    }
}

public class JsonObject
{
    public string Value { get; set; }
    public string Type { get; set; }
}

Выход:

(String) "foo bar"
(Real) 3.14
(JsonData) {"Something":"Test","Items":["A","B","C"]}
(Integer) 42
person Brian Rogers    schedule 06.09.2015
comment
Спасибо за решение! За это время я решил повторно сериализовать, как вы предложили. Но без преобразователя. Ваше решение немного чище! - person Florian Segginger; 08.09.2015

попробуйте с этим:

public class JsonObject
{
    public object Value { get; set; }
    public object Type { get; set; }
}
person Amit Kumar Ghosh    schedule 04.09.2015
comment
Спасибо, я уже пробовал, и да, он правильно десериализуется. Однако у меня проблема в том, что мне действительно нужно, чтобы Value было строкой, а не object. Это то, что ожидают верхние части моего кода! - person Florian Segginger; 04.09.2015
comment
ваше требование довольно странное. Плюс with "Value": 3.14, Value не string. - person Amit Kumar Ghosh; 04.09.2015
comment
Я знаю, что это не струна. Я хочу сказать, что верхние слои будут знать, что делать с "3.14" или "\"Hello\"". - person Florian Segginger; 04.09.2015
comment
ну тогда они тоже могут знать о object. - person Amit Kumar Ghosh; 04.09.2015