2009-07-08 8 views
72

요약 : JavaScriptSerializer.Deserialize를 사용할 때 JSON 데이터의 필드 이름을 .Net 객체의 필드 이름에 매핑하는 방법은 무엇입니까?JavaScriptSerializer.Deserialize - 필드 이름을 변경하는 방법

긴 버전 : 나는 다음과 같은 JSON 데이터 (닷넷 코딩되지 않음) 서버 API

{"user_id":1234, "detail_level":"low"} 

에서 나에게오고있는 I가 그것을위한 다음 C# 개체 :

[Serializable] 
public class DataObject 
{ 
    [XmlElement("user_id")] 
    public int UserId { get; set; } 

    [XmlElement("detail_level")] 
    public DetailLevel DetailLevel { get; set; } 
} 

여기서 DetailLevel은 값 중 하나 인 "Low"가있는 열거 형입니다.

[TestMethod] 
public void DataObjectSimpleParseTest() 
{ 
    JavaScriptSerializer serializer = new JavaScriptSerializer(); 
    DataObject dataObject = serializer.Deserialize<DataObject>(JsonData); 

    Assert.IsNotNull(dataObject); 
    Assert.AreEqual(DetailLevel.Low, dataObject.DetailLevel); 
    Assert.AreEqual(1234, dataObject.UserId); 
} 

을 그리고 그 필드에 데이터가 없기 때문에 마지막 두, 실패 주장 :

이 테스트는 실패합니다. 내가

{"userid":1234, "detaillevel":"low"} 

에 JSON 데이터를 변경하면 그 다음은 전달합니다. 하지만 서버의 동작을 변경할 수 없으며 클라이언트 클래스가 C# 관용구에 잘 명명 된 속성을 갖기를 원합니다. JSON에 LINQ를 사용할 수 없기 때문에 Silverlight 외부에서 작업하고 싶습니다. XmlElement 태그가 효과가없는 것처럼 보입니다. 나는 그들이 전혀 관련이 없다는 생각을 어디에서 얻었는지 모르지만 아마 그렇지 않을 것이다.

JavaScriptSerializer에서 필드 이름 매핑을 어떻게 수행합니까? 전혀 끝낼 수 있습니까?

+1

나는 'JavaScriptSerializer'를 싫어한다. 'JwtSecurityTokenHandler'는 정적'JsonExtensions.Serializer' 속성을 통해이를 사용합니다. 즉, 런타임 중에 변경하면 변경되지 않을 것으로 기대하는 다른 코드에 영향을 줄 수 있습니다. 불행히도 이러한 수업 중 많은 부분이 그런 방식입니다. : – NathanAldenSr

답변

72

DataContractJsonSerializer 클래스를 사용하여 다시 시도해 보았습니다.

코드는 다음과 같습니다 : 이것은 해결

using System.Runtime.Serialization; 

[DataContract] 
public class DataObject 
{ 
    [DataMember(Name = "user_id")] 
    public int UserId { get; set; } 

    [DataMember(Name = "detail_level")] 
    public string DetailLevel { get; set; } 
} 

그리고 시험은 다음과 같습니다

using System.Runtime.Serialization.Json; 

[TestMethod] 
public void DataObjectSimpleParseTest() 
{ 
     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DataObject)); 

     MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(JsonData)); 
     DataObject dataObject = serializer.ReadObject(ms) as DataObject; 

     Assert.IsNotNull(dataObject); 
     Assert.AreEqual("low", dataObject.DetailLevel); 
     Assert.AreEqual(1234, dataObject.UserId); 
} 

유일한 단점은 내가 문자열로 열거에서 DetailLevel을 변경 한 것입니다 - 열거 형을 그대로 유지하면 DataContractJsonSerializer는 숫자 값을 읽지 못하고 실패합니다. 자세한 내용은 DataContractJsonSerializer and Enums을 참조하십시오.

제 생각에 이것은 JavaScriptSerializer가 올바르게 처리하는 것처럼 매우 가난합니다.

[DataContract] 
public enum DetailLevel 
{ 
    [EnumMember(Value = "low")] 
    Low, 
    ... 
} 

이것은 또한 실버 라이트에서 작동하는 것 같다 :이 동작을 변경하지 않습니다

System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type DataObject. The value 'low' cannot be parsed as the type 'Int64'. ---> 
System.Xml.XmlException: The value 'low' cannot be parsed as the type 'Int64'. ---> 
System.FormatException: Input string was not in a correct format 

그리고이 같은 열거 최대 표시 : 이것은 당신이 열거로 문자열을 구문 분석을 시도 취득의 예외입니다 .

+1

그레이트 솔루션! With .Net 4.5 그냥 일반 [DataMember] 선언 ([EnumMember] 등)에 대한 필요성과 열거 형 멤버에 대한 잘 작동하는 것 –

+0

당신의 JsonData에 뭐가 있니? , 나는 XML을 기대하는 것처럼 Serializer가 루트 요소를 기대한다는 SerializationException을 얻는다. 내 JSON 데이터는 { "user": "THEDOMAIN \\ MDS", "password": "JJJJ"} –

13

Json.NET 원하는대로 할 수 있습니다. DataContract/DataMember 특성을 읽는 기능과 속성 이름을 변경하는 기능을 자체적으로 지원합니다. 또한 열거 형 값을 숫자가 아닌 이름으로 직렬화하는 StringEnumConverter 클래스가 있습니다.

5

JavaScriptConverter에서 상속받은 클래스를 만듭니다.

Methods-

  1. 직렬화
  2. 역 직렬화

속성 인

  1. SupportedTypes
  2. : 당신은 다음 세 가지를 구현해야합니다

직렬화 및 비 직렬화 프로세스를보다 세부적으로 제어해야하는 경우 JavaScriptConverter 클래스를 사용할 수 있습니다. 당신이 어떤 속성에 어떤 이름을 매핑 할 수 있습니다 JavaScriptConverter 정의를 작성하여

Here is a link for further information

20

JavaScriptSerializer serializer = new JavaScriptSerializer(); 
serializer.RegisterConverters(new JavaScriptConverter[] { new MyCustomConverter() }); 

DataObject dataObject = serializer.Deserialize<DataObject>(JsonData); 
. 그러나 그것은 이상적인 것보다 적은지도를 직접 코딩해야합니다.

public class DataObjectJavaScriptConverter : JavaScriptConverter 
{ 
    private static readonly Type[] _supportedTypes = new[] 
    { 
     typeof(DataObject) 
    }; 

    public override IEnumerable<Type> SupportedTypes 
    { 
     get { return _supportedTypes; } 
    } 

    public override object Deserialize(IDictionary<string, object> dictionary, 
             Type type, 
             JavaScriptSerializer serializer) 
    { 
     if(type == typeof(DataObject)) 
     { 
      var obj = new DataObject(); 
      if(dictionary.ContainsKey("user_id")) 
       obj.UserId = serializer.ConvertToType<int>( 
              dictionary["user_id"]); 
      if(dictionary.ContainsKey("detail_level")) 
       obj.DetailLevel = serializer.ConvertToType<DetailLevel>(
              dictionary["detail_level"]); 

      return obj; 
     } 

     return null; 
    } 

    public override IDictionary<string, object> Serialize( 
      object obj, 
      JavaScriptSerializer serializer) 
    { 
     var dataObj = obj as DataObject; 
     if(dataObj != null) 
     { 
      return new Dictionary<string,object> 
      { 
       {"user_id", dataObj.UserId }, 
       {"detail_level", dataObj.DetailLevel } 
      } 
     } 
     return new Dictionary<string, object>(); 
    } 
} 

그런 다음과 같이 역 직렬화 할 수 있습니다

var serializer = new JavaScriptSerializer(); 
serialzer.RegisterConverters(new[]{ new DataObjectJavaScriptConverter() }); 
var dataObj = serializer.Deserialize<DataObject>(json); 
4

나는 아래와 같이 사용 Newtonsoft.Json을 사용했다. 객체를 생성하십시오 :

public class WorklistSortColumn 
    { 
    [JsonProperty(PropertyName = "field")] 
    public string Field { get; set; } 

    [JsonProperty(PropertyName = "dir")] 
    public string Direction { get; set; } 

    [JsonIgnore] 
    public string SortOrder { get; set; } 
    } 

이제 아래 보이는 것처럼 Json 객체로 직렬화하려면 아래 메소드를 호출하십시오.

string sortColumn = JsonConvert.SerializeObject(worklistSortColumn); 
11

당신은 아주 쉽게 추가 할 수 있습니다 그러나 JavaScriptSerializer에 속성 이름을 변경에 대한 표준 지원되지 않습니다 자신의 :

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Web.Script.Serialization; 
using System.Reflection; 

public class JsonConverter : JavaScriptConverter 
{ 
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) 
    { 
     List<MemberInfo> members = new List<MemberInfo>(); 
     members.AddRange(type.GetFields()); 
     members.AddRange(type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)); 

     object obj = Activator.CreateInstance(type); 

     foreach (MemberInfo member in members) 
     { 
      JsonPropertyAttribute jsonProperty = (JsonPropertyAttribute)Attribute.GetCustomAttribute(member, typeof(JsonPropertyAttribute)); 

      if (jsonProperty != null && dictionary.ContainsKey(jsonProperty.Name)) 
      { 
       SetMemberValue(serializer, member, obj, dictionary[jsonProperty.Name]); 
      } 
      else if (dictionary.ContainsKey(member.Name)) 
      { 
       SetMemberValue(serializer, member, obj, dictionary[member.Name]); 
      } 
      else 
      { 
       KeyValuePair<string, object> kvp = dictionary.FirstOrDefault(x => string.Equals(x.Key, member.Name, StringComparison.InvariantCultureIgnoreCase)); 

       if (!kvp.Equals(default(KeyValuePair<string, object>))) 
       { 
        SetMemberValue(serializer, member, obj, kvp.Value); 
       } 
      } 
     } 

     return obj; 
    } 


    private void SetMemberValue(JavaScriptSerializer serializer, MemberInfo member, object obj, object value) 
    { 
     if (member is PropertyInfo) 
     { 
      PropertyInfo property = (PropertyInfo)member;     
      property.SetValue(obj, serializer.ConvertToType(value, property.PropertyType), null); 
     } 
     else if (member is FieldInfo) 
     { 
      FieldInfo field = (FieldInfo)member; 
      field.SetValue(obj, serializer.ConvertToType(value, field.FieldType)); 
     } 
    } 


    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) 
    { 
     Type type = obj.GetType(); 
     List<MemberInfo> members = new List<MemberInfo>(); 
     members.AddRange(type.GetFields()); 
     members.AddRange(type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)); 

     Dictionary<string, object> values = new Dictionary<string, object>(); 

     foreach (MemberInfo member in members) 
     { 
      JsonPropertyAttribute jsonProperty = (JsonPropertyAttribute)Attribute.GetCustomAttribute(member, typeof(JsonPropertyAttribute)); 

      if (jsonProperty != null) 
      { 
       values[jsonProperty.Name] = GetMemberValue(member, obj); 
      } 
      else 
      { 
       values[member.Name] = GetMemberValue(member, obj); 
      } 
     } 

     return values; 
    } 

    private object GetMemberValue(MemberInfo member, object obj) 
    { 
     if (member is PropertyInfo) 
     { 
      PropertyInfo property = (PropertyInfo)member; 
      return property.GetValue(obj, null); 
     } 
     else if (member is FieldInfo) 
     { 
      FieldInfo field = (FieldInfo)member; 
      return field.GetValue(obj); 
     } 

     return null; 
    } 


    public override IEnumerable<Type> SupportedTypes 
    { 
     get 
     { 
      return new[] { typeof(DataObject) }; 
     } 
    } 
} 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 
public class JsonPropertyAttribute : Attribute 
{ 
    public JsonPropertyAttribute(string name) 
    { 
     Name = name; 
    } 

    public string Name 
    { 
     get; 
     set; 
    } 
} 

DataObject 클래스는 다음이된다 :

public class DataObject 
{ 
    [JsonProperty("user_id")] 
    public int UserId { get; set; } 

    [JsonProperty("detail_level")] 
    public DetailLevel DetailLevel { get; set; } 
} 

나는이 힘을 appreicate 조금 늦었지만 DataContractJsonSerializer이 아닌 다른 사람들이 JavaScriptSerializer을 사용하고 싶어한다고 생각할 수도 있습니다.

+1

이다. 나는이 코드를 JsonConverter 과 같은 제네릭과 함께 사용했다.이 클래스는 모든 유형에서 사용할 수있다. –

0

내 요구 사항은 포함 :

  • 은 3.5
  • 외부를 추가 안됩니다 목표로한다
  • 이 colelctions을 처리해야 서비스에서받은 형식으로 날짜를 역 직렬화해야 dataContracts 존중해야한다 의존성, 특히 Newtonsoft가 아닙니다. (직접 배포 패키지를 만들고 있습니다)
  • 내 손으로 해결할 수 있습니다.

내 솔루션은 SimpleJson (https://github.com/facebook-csharp-sdk/simple-json)을 사용하는 것이 었습니다.

너겟 패키지를 통해 설치할 수 있지만 필자는 프로젝트에 MIT 라이선스가있는 SimpleJson.cs 파일 하나만 포함하고 참조했습니다.

이 정보가 도움이되기를 바랍니다.

- (내가 어떤 :) 생각할 수 없다) 어떤 이유로 Newtonsoft Json.Net 또는 DataContractJsonSerializer 가고 싶어하지 않는 사람들을 위해
1

, 여기 string 변환 DataContractenum을 지원 JavaScriptConverter의 구현입니다
public class DataContractJavaScriptConverter : JavaScriptConverter 
    { 
     private static readonly List<Type> _supportedTypes = new List<Type>(); 

     static DataContractJavaScriptConverter() 
     { 
      foreach (Type type in Assembly.GetExecutingAssembly().DefinedTypes) 
      { 
       if (Attribute.IsDefined(type, typeof(DataContractAttribute))) 
       { 
        _supportedTypes.Add(type); 
       } 
      } 
     } 

     private bool ConvertEnumToString = false; 

     public DataContractJavaScriptConverter() : this(false) 
     { 
     } 

     public DataContractJavaScriptConverter(bool convertEnumToString) 
     { 
      ConvertEnumToString = convertEnumToString; 
     } 

     public override IEnumerable<Type> SupportedTypes 
     { 
      get { return _supportedTypes; } 
     } 

     public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) 
     { 
      if (Attribute.IsDefined(type, typeof(DataContractAttribute))) 
      { 
       try 
       { 
        object instance = Activator.CreateInstance(type); 

        IEnumerable<MemberInfo> members = ((IEnumerable<MemberInfo>)type.GetFields()) 
         .Concat(type.GetProperties().Where(property => property.CanWrite && property.GetIndexParameters().Length == 0)) 
         .Where((member) => Attribute.IsDefined(member, typeof(DataMemberAttribute))); 
        foreach (MemberInfo member in members) 
        { 
         DataMemberAttribute attribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute)); 
         object value; 
         if (dictionary.TryGetValue(attribute.Name, out value) == false) 
         { 
          if (attribute.IsRequired) 
          { 
           throw new SerializationException(String.Format("Required DataMember with name {0} not found", attribute.Name)); 
          } 
          continue; 
         } 
         if (member.MemberType == MemberTypes.Field) 
         { 
          FieldInfo field = (FieldInfo)member; 
          object fieldValue; 
          if (ConvertEnumToString && field.FieldType.IsEnum) 
          { 
           fieldValue = Enum.Parse(field.FieldType, value.ToString()); 
          } 
          else 
          { 
           fieldValue = serializer.ConvertToType(value, field.FieldType); 
          } 
          field.SetValue(instance, fieldValue); 
         } 
         else if (member.MemberType == MemberTypes.Property) 
         { 
          PropertyInfo property = (PropertyInfo)member; 
          object propertyValue; 
          if (ConvertEnumToString && property.PropertyType.IsEnum) 
          { 
           propertyValue = Enum.Parse(property.PropertyType, value.ToString()); 
          } 
          else 
          { 
           propertyValue = serializer.ConvertToType(value, property.PropertyType); 
          } 
          property.SetValue(instance, propertyValue); 
         } 
        } 
        return instance; 
       } 
       catch (Exception) 
       { 
        return null; 
       } 
      } 
      return null; 
     } 

     public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) 
     { 
      Dictionary<string, object> dictionary = new Dictionary<string, object>(); 
      if (obj != null && Attribute.IsDefined(obj.GetType(), typeof(DataContractAttribute))) 
      { 
       Type type = obj.GetType(); 
       IEnumerable<MemberInfo> members = ((IEnumerable<MemberInfo>)type.GetFields()) 
        .Concat(type.GetProperties().Where(property => property.CanRead && property.GetIndexParameters().Length == 0)) 
        .Where((member) => Attribute.IsDefined(member, typeof(DataMemberAttribute))); 
       foreach (MemberInfo member in members) 
       { 
        DataMemberAttribute attribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute)); 
        object value; 
        if (member.MemberType == MemberTypes.Field) 
        { 
         FieldInfo field = (FieldInfo)member; 
         if (ConvertEnumToString && field.FieldType.IsEnum) 
         { 
          value = field.GetValue(obj).ToString(); 
         } 
         else 
         { 
          value = field.GetValue(obj); 
         } 
        } 
        else if (member.MemberType == MemberTypes.Property) 
        { 
         PropertyInfo property = (PropertyInfo)member; 
         if (ConvertEnumToString && property.PropertyType.IsEnum) 
         { 
          value = property.GetValue(obj).ToString(); 
         } 
         else 
         { 
          value = property.GetValue(obj); 
         } 
        } 
        else 
        { 
         continue; 
        } 
        if (dictionary.ContainsKey(attribute.Name)) 
        { 
         throw new SerializationException(String.Format("More than one DataMember found with name {0}", attribute.Name)); 
        } 
        dictionary[attribute.Name] = value; 
       } 
      } 
      return dictionary; 
     } 
    } 

참고 :이 DataContractJavaScriptConverter은 배치 된 어셈블리에 정의 된 클래스 만 DataContract을 처리합니다. 별도의 어셈블리에서 클래스를 원할 경우 정적 생성자에 따라 _supportedTypes 목록을 수정하십시오.

JavaScriptSerializer serializer = new JavaScriptSerializer(); 
    serializer.RegisterConverters(new JavaScriptConverter[] { new DataContractJavaScriptConverter(true) }); 
    DataObject dataObject = serializer.Deserialize<DataObject>(JsonData); 

DataObject 클래스는 다음과 같이 보일 것이다 - - 다음과 같이

사용할 수 있습니다

using System.Runtime.Serialization; 

    [DataContract] 
    public class DataObject 
    { 
     [DataMember(Name = "user_id")] 
     public int UserId { get; set; } 

     [DataMember(Name = "detail_level")] 
     public string DetailLevel { get; set; } 
    } 

이 솔루션은 DataMember 속성에 의해 지원 EmitDefaultValueOrder 속성을 처리하지 않음을 유의하시기 바랍니다 .

+0

ASP.NET json (ajax를 WebMethod로 변환)에서 .NET 객체로 변환 할 때 ASP.NET은'JavascriptSerializer'를 사용하므로 사용자는 클라이언트 데이터를 처리하고자하는 웹 사이트에서 일하는 것은 선택의 여지가 없지만 Microsoft의 이온. 직렬화를 위해 우리는 물론 Newtonsoft를 사용할 수 있지만 비 직렬화하려면이 기능이 필요합니다. 출처 : http://referencesource.microsoft.com/#System.Web.Extensions/Script/Serialization/ObjectConverter.cs,809b4f13600ad28c,references –

관련 문제