c# serialize Serializar enumeración a cadena




serialize json c# newtonsoft (8)

El formateador JSON tiene un comportamiento muy especializado cuando se trabaja con enumeraciones; los atributos normales del Contrato de datos se ignoran y trata su enumeración como un número, no como la cadena más legible para los humanos que se esperaría con otros formatos. Si bien esto facilita el manejo de las enumeraciones de tipo bandera, hace que sea mucho más difícil trabajar con la mayoría de los otros tipos.

Desde MSDN :

Los valores de los miembros de la enumeración se tratan como números en JSON , que es diferente de la forma en que se tratan en los contratos de datos, donde se incluyen como nombres de miembros. Para obtener más información sobre el tratamiento del contrato de datos, consulte Tipos de enumeración en contratos de datos .

  • Por ejemplo, si tiene public enum Color {red, green, blue, yellow, pink} , la serialización amarilla produce el número 3 y no la cadena "amarilla".

  • Todos los miembros de la enumeración son serializables. Los atributos EnumMemberAttribute y NonSerializedAttribute se ignoran si se utilizan.

  • Es posible deserializar un valor de enumeración inexistente; por ejemplo, el valor 87 puede ser deserializado en la enumeración de Color anterior, aunque no haya un nombre de color correspondiente definido.

  • Una enumeración de banderas no es especial y se trata igual que cualquier otra enumeración.

La única forma práctica de resolver esto, para permitir que los usuarios finales especifiquen una cadena en lugar de un número, es no usar la enumeración en su contrato. En su lugar, la respuesta práctica es reemplazar su enumeración con una cadena y realizar una validación interna en el valor de tal manera que se pueda analizar en una de las representaciones de enumeración válidas.

De forma alternativa (aunque no sea por un corazón), puedes reemplazar el formateador JSON por el tuyo, que respetaría las enumeraciones de la misma manera que otros formateadores.

Tengo una enumeración:

public enum Action {
    Remove=1,
    Add=2
}

Y una clase:

[DataContract]
public class Container {
    [DataMember]
    public Action Action {get; set;}
}

Cuando serializo la instancia de Container to json obtengo: {Action:1} (en caso de que Acción sea Eliminar).

Me gustaría obtener: {Action:Remove} (en lugar de int, necesito ToString formulario de la enumeración)

¿Puedo hacerlo sin agregar otro miembro a la clase?


He puesto una solución a esto usando la biblioteca Newtonsoft.Json . Soluciona el problema de la enumeración y también hace que el manejo de errores sea mucho mejor, y funciona en los servicios alojados en IIS en lugar de en los propios. No requiere cambios ni nada especial para agregarse a sus clases de DataContract . Es bastante código, por lo que puede encontrarlo en GitHub aquí: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

Tiene que agregar algunas entradas a su Web.config para que funcione, puede ver un archivo de ejemplo aquí: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config


Aquí hay una manera simple de hacer esto:

JsonConvert.SerializeObject(myObject, Formatting.Indented, new StringEnumConverter());

Usando Json.Net , puede definir un StringEnumConverter personalizado como

public class MyStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is Action)
        {
            writer.WriteValue(Enum.GetName(typeof(Action),(Action)value));// or something else
            return;
        }

        base.WriteJson(writer, value, serializer);
    }
}

y serializar como

string json=JsonConvert.SerializeObject(container,new MyStringEnumConverter());

Para propósitos de serialización, si el contenedor no debe contener propiedades de enumeración pero están llenas, puede usar el método de extensión a continuación.

Definición de contenedor

public class Container
{
    public string Action { get; set; }
}

Definición de enumeración

public enum Action {
    Remove=1,
    Add=2
}

Codigo en vistas

@Html.DropDownListFor(model => model.Action, typeof (Action))

Método de extensión

/// <summary>
/// Returns an HTML select element for each property in the object that is represented by the specified expression using the given enumeration list items.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TProperty">The type of the value.</typeparam>
/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
/// <param name="expression">An expression that identifies the object that contains the properties to display.</param>
/// <param name="enumType">The type of the enum that fills the drop box list.</param>
/// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression, Type enumType)
{
    var values = from Enum e in Enum.GetValues(enumType)
                    select new { Id = e, Name = e.ToString() };

    return htmlHelper.DropDownListFor(expression, new SelectList(values, "Id", "Name"));
}

Simplemente puede agregar el atributo:

    [Newtonsoft.Json.Converters.JsonConverter(typeof(StringEnumConverter))] 

a la propiedad enum que no se serializa como una cadena.

o si tiene en mente un formateo más exótico, podría usar los atributos como se indica a continuación para decirle al serializador JSON que serialice solo la propiedad que ha formateado como desea. Depende un poco del resto de tu implementación. También reconoce el atributo DataMember en una propiedad.

[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class Container
{
    public Action Action { get; set; }

    [JsonProperty(PropertyName = "Action")]
    public string ActionString
    {
        get
        {
            return Action.ToString();
        }
    }
}

He estado usando una muy buena solución al usar una propiedad privada auxiliar para la serialización y deserialización que funciona para la serialización por el nombre de miembro de la enumeración o por el valor del EnumMemberAttribute .

Las mayores ventajas que veo, son que:

  • No necesitas ajustar con el serializador.
  • Toda la lógica de serialización está contenida en el objeto de datos.
  • Puede ocultar su propiedad auxiliar configurando su modificador de accesibilidad como privado, ya que los DataContractSerializers pueden obtener y establecer propiedades privadas
  • Puede serializar la enumeración como una string lugar de un int

Tu clase se verá así:

[DataContract]
public class SerializableClass {
    public Shapes Shape {get; set;} //Do not use the DataMemberAttribute in the public property

    [DataMember(Name = "shape")]
    private string ShapeSerialization // Notice the PRIVATE here!
    {
        get { return EnumHelper.Serialize(this.Shape); }
        set { this.Shape = EnumHelper.Deserialize<Shapes>(value); }
    }
}

EnumHelper.cs

/* Available at: https://gist.github.com/mniak/a4d09264ad1ca40c489178325b98935b */
public static class EnumHelper
{
    public static string Serialize<TEnum>(TEnum value)
    {
        var fallback = Enum.GetName(typeof(TEnum), value);
        var member = typeof(TEnum).GetMember(value.ToString()).FirstOrDefault();
        if (member == null)
            return fallback;
        var enumMemberAttributes = member.GetCustomAttributes(typeof(EnumMemberAttribute), false).Cast<EnumMemberAttribute>().FirstOrDefault();
        if (enumMemberAttributes == null)
            return fallback;
        return enumMemberAttributes.Value;
    }
    public static TEnum Deserialize<TEnum>(string value) where TEnum : struct
    {
        TEnum parsed;
        if (Enum.TryParse<TEnum>(value, out parsed))
            return parsed;

        var found = typeof(TEnum).GetMembers()
            .Select(x => new
            {
                Member = x,
                Attribute = x.GetCustomAttributes(typeof(EnumMemberAttribute), false).OfType<EnumMemberAttribute>().FirstOrDefault()
            })
            .FirstOrDefault(x => x.Attribute?.Value == value);
        if (found != null)
            return (TEnum)Enum.Parse(typeof(TEnum), found.Member.Name);
        return default(TEnum);
    }
}

Intenta usar

public enum Action {
    [EnumMember(Value = "Remove")]
    Remove=1,
    [EnumMember(Value = "Add")]
    Add=2
}

No estoy seguro de si esto se adapta a tu caso, así que podría estar equivocado.

Se describe aquí: http://msdn.microsoft.com/en-us/library/aa347875.aspx





datacontract