Управлять форматом JSON Serialization пользовательского типа в .NET - PullRequest
3 голосов
/ 08 апреля 2010

У меня есть класс PhoneNumber, в котором хранится нормализованная строка, и я определил неявные операторы для строки <-> Phone, чтобы упростить обработку PhoneNumber как строки. Я также переопределил метод ToString (), чтобы всегда возвращать очищенную версию числа (без дефисов, скобок или пробелов). В любых представлениях, где отображается номер, я явно вызываю phone.Format ().

Проблема здесь заключается в сериализации объекта, имеющего PhoneNumber, в JSON; JavaScriptSerializer сериализует его как [object Object].

Я хочу сериализовать ее в виде строки в формате (555)555-5555.

Я смотрел на написание пользовательского JavaScriptConverter, но метод JavaScriptConverter.Serialize () возвращает словарь пар имя-значение . Я не хочу, чтобы PhoneNumber обрабатывался как объект с полями, я хочу просто сериализовать его как строку.

Ответы [ 2 ]

1 голос
/ 08 апреля 2010

Стоит рассмотреть тот JSON, который вы хотите.

Если у вас есть этот класс

class Person
{
    public string Name { get; set; }
    public PhoneNumber HomePhone { get; set; }
}

Вы хотите, чтобы это сериализовалось в JSON, вот так

{ "Name":"Joe", "HomePhone": "555-555-555" }

Но вы получаете JSON, что-то вроде этого

{ "Name":"Joe","HomePhone": {"Number": "555-555-555"} }

-

Чтобы понять, почему это так, учтите, что свойство Число из Персона является объектом . JSON ожидает, что по крайней мере {} обернет объект - или более точно, набор имен / значений в {}.

Если вы действительно хотите использовать прежний синтаксис JSON, вам нужно зарегистрировать пользовательский преобразователь для объекта Person , чтобы можно было убедить его сериализовать и десериализовать в виде строки. (см. пример кода ниже).

Однако я бы порекомендовал вам просто принять, что, поскольку PhoneNumber является объектом, он соответствует словарю имя / значение при сериализации в JSON, и принимаете, что JSON будет выглядеть немного менее «чистым», чем в идеале хочу.

FWIW вот пример кода, который достигает вашей первоначальной цели (не рекомендуемый подход) ..

class Person
{
    public string Name { get; set; }
    public PhoneNumber HomeNumber { get; set; }
}

struct PhoneNumber
{
    private string _number;
    public PhoneNumber(string number)
    {
        _number = number;
    }

    public override string ToString()
    {
        return _number;
    }
}

class PersonConverter : JavaScriptConverter
{
    public override IEnumerable<Type> SupportedTypes
    {
        get { yield return typeof(Person); }
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        Person person = obj as Person;
        if (person != null)
        {
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict["Name"] = person.Name;
            dict["HomeNumber"] = person.HomeNumber.ToString();
            return dict;
        }
        return new Dictionary<string, object>();
    }

    public override object Deserialize(IDictionary<string, object> dict, Type type, JavaScriptSerializer serializer)
    {
        if (dict == null)
            throw new ArgumentNullException("dict");

        if (type == typeof(Person))
        {
            // Deserialize the Person's single property.
            string name = (string)dict["Name"];
            string homeNumber = (string)dict["HomeNumber"];

            // Create the instance to deserialize into.
            Person person = new Person()
            {
                Name = name,
                HomeNumber = new PhoneNumber(homeNumber)
            };
            return person;
        }
        return null;
    }
}

class Program
{
    static void Main(string[] args)
    {
        PhoneNumber number = new PhoneNumber("555 555");
        Person joe = new Person() { Name = "Joe", HomeNumber = number };

        JavaScriptSerializer serializer = new JavaScriptSerializer();
        serializer.RegisterConverters(new JavaScriptConverter[] { new PersonConverter() });
        Console.Out.WriteLine(serializer.Serialize(joe));
    }
}
0 голосов
/ 20 февраля 2018

Я знаю, что это старо, но при поиске я наткнулся на это , когда искал ответ на вопрос ОП. Представленный ответ заключается в создании именованного типа, который наследуется от URI и реализует интерфейс IDictionary. Когда он затем сериализуется, .Net распознает наследование URI и выводит строку вместо коллекции объектов. Это оставляет вас с большим почти бесполезным классом, просто необходимым для передачи словаря из переопределенного метода сериализации.

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

...