source

경우에 따라 어레이 또는 객체에 따라서는 JSON의 시리얼화를 해제한다.

bestscript 2023. 3. 1. 11:16

경우에 따라 어레이 또는 객체에 따라서는 JSON의 시리얼화를 해제한다.

JSON을 사용하여 Facebook에서 반환된 데이터를 역직렬화하는 데 문제가 있습니다.NET 라이브러리

단순한 벽 기둥에서 돌아온 JSON은 다음과 같습니다.

{
    "attachment":{"description":""},
    "permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}

사진을 찍기 위해 반환된 JSON은 다음과 같습니다.

"attachment":{
        "media":[
            {
                "href":"http://www.facebook.com/photo.php?fbid=12345",
                "alt":"",
                "type":"photo",
                "src":"http://photos-b.ak.fbcdn.net/hphotos-ak-ash1/12345_s.jpg",
                "photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}}
        ],

모든 것이 잘 작동하고 나는 아무 문제가 없다.모바일 클라이언트로부터 다음과 같은 JSON을 사용한 심플한 벽면 포스트를 발견했는데, 이 1개의 포스트로 디시리얼라이즈가 실패합니다.

"attachment":
    {
        "media":{},
        "name":"",
        "caption":"",
        "description":"",
        "properties":{},
        "icon":"http://www.facebook.com/images/icons/mobile_app.gif",
        "fb_object_type":""
    },
"permalink":"http://www.facebook.com/1234"

다음 클래스로서 역직렬화하겠습니다.

public class FacebookAttachment
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public string Href { get; set; }
        public FacebookPostType Fb_Object_Type { get; set; }
        public string Fb_Object_Id { get; set; }

        [JsonConverter(typeof(FacebookMediaJsonConverter))]
        public List<FacebookMedia> { get; set; }

        public string Permalink { get; set; }
    }

FacebookMediaJsonConverter를 사용하지 않으면 "Cannot deserialize JSON object to type 'System" (JSON 개체를 직렬화할 수 없습니다)라는 오류가 나타납니다.컬렉션포괄적인.리스트1 [ Facebook Media ] 。JSON에서 미디어는 컬렉션이 아니기 때문에 말이 됩니다.

비슷한 문제를 설명하는 이 게시물을 발견했기 때문에 다음 경로를 시도했습니다.JSON을 역직렬화합니다.값이 배열일 때도 있고, " (공백 문자열)일 때도 있습니다.

컨버터는 다음과 같습니다.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
     if (reader.TokenType == JsonToken.StartArray)
          return serializer.Deserialize<List<FacebookMedia>>(reader);
     else
          return null;
}

정상적으로 동작하지만, 새로운 예외가 있습니다.

내부 Json SerializerInternalReader.cs, Create ValueInternal(): 오브젝트를 역직렬화하는 동안 예기치 않은 토큰이 발생했습니다.PropertyName

독자의 가치값은 "permalink"입니다.스위치로 JsonToken에 대한 케이스가 없다는 것을 알 수 있습니다.Property Name(속성명)

컨버터에서 다르게 해야 할 일이 있나요?도와주셔서 감사합니다.

이 경우의 처리 방법에 대한 자세한 설명은 "Using a Custom Json Converter to fixed JSON results" (커스텀 Json 컨버터를 사용하여 잘못된 JSON 결과를 수정함)를 참조하십시오.

요약하면 기본 JSON을 확장할 수 있습니다.NET 컨버터 실행

  1. 이슈를 가진 속성에 주석 달기

    [JsonConverter(typeof(SingleValueArrayConverter<OrderItem>))]
    public List<OrderItem> items;
    
  2. 단일 개체에 대해 원하는 유형의 목록을 반환하도록 변환기를 확장합니다.

    public class SingleValueArrayConverter<T> : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            object retVal = new Object();
            if (reader.TokenType == JsonToken.StartObject)
            {
                T instance = (T)serializer.Deserialize(reader, typeof(T));
                retVal = new List<T>() { instance };
            } else if (reader.TokenType == JsonToken.StartArray) {
                retVal = serializer.Deserialize(reader, objectType);
            }
            return retVal;
        }
    
        public override bool CanConvert(Type objectType)
        {
            return true;
        }
    }
    

기사에서 언급한 바와 같이 이 확장자는 완전히 일반적이지는 않지만 목록을 받아도 상관없습니다.

JSON 개발자.NET은 결국 프로젝트 코드플렉스 사이트를 지원하게 되었습니다.해결책은 다음과 같습니다.

문제는 JSON 오브젝트일 때 Atribute를 지나치지 않았다는 것입니다.다음은 올바른 코드입니다.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (reader.TokenType == JsonToken.StartArray)
    {
        return serializer.Deserialize<List<FacebookMedia>>(reader);
    }
    else
    {
        FacebookMedia media = serializer.Deserialize<FacebookMedia>(reader);
        return new List<FacebookMedia>(new[] {media});
    }
}

제임스는 또한 위의 방법에 대한 유닛 테스트를 제공할 정도로 친절했습니다.

위의 Camilo Martinez의 답변에 따르면, 이것은 범용 버전을 사용하는 보다 현대적이고, 안전하고, 보다 슬림하고, 완전한 접근법입니다.JsonConverterC# 8.0 입니다.또한 질문에 따라 예상된 2개 이외의 토큰에 대해서도 예외를 발생시킵니다.코드가 필요 이상으로 동작하지 않으면 예기치 않은 데이터를 잘못 처리하여 향후 버그가 발생할 위험이 있습니다.

internal class SingleObjectOrArrayJsonConverter<T> : JsonConverter<ICollection<T>> where T : class, new()
{
    public override void WriteJson(JsonWriter writer, ICollection<T> value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value.Count == 1 ? (object)value.Single() : value);
    }

    public override ICollection<T> ReadJson(JsonReader reader, Type objectType, ICollection<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        return reader.TokenType switch
        {
            JsonToken.StartObject => new Collection<T> {serializer.Deserialize<T>(reader)},
            JsonToken.StartArray => serializer.Deserialize<ICollection<T>>(reader),
            _ => throw new ArgumentOutOfRangeException($"Converter does not support JSON token type {reader.TokenType}.")
        };
    }
}

그런 다음 다음과 같이 속성을 장식합니다.

[JsonConverter(typeof(SingleObjectOrArrayJsonConverter<OrderItem>))]
public ICollection<OrderItem> items;

을 '있다'에서 '로 바꿨습니다.List<>로로 합니다.ICollection<>으로 이 JSON POCO가 이 타입으로 되어 있으면List<> 치환해 주세요.ICollection ★★★★★★★★★★★★★★★★★」CollectionList상기의 모든 코드로.

시스템을 살펴봅니다.런타임c# 프레임워크의 시리얼화 네임스페이스를 사용하면 원하는 위치에 빠르게 도달할 수 있습니다.

만약 당신이 원한다면 이 프로젝트에서 몇 가지 샘플 코드를 체크할 수 있습니다(내 작업을 연결하려고 하는 것이 아니라 나는 당신이 하고 있는 일을 거의 정확하게 끝냈지만 다른 소스 API로).

도움이 됐으면 좋겠네요

.Net 프레임워크

using Newtonsoft.Json;
using System.IO;   

public Object SingleObjectOrArrayJson(string strJson)
{   
    if(String.IsNullOrEmpty(strJson))
    {
       //Example
       strJson= @"{
        'CPU': 'Intel',
        'PSU': '500W',
        'Drives': [
          'DVD read/writer'
          /*(broken)*/,
          '500 gigabyte hard drive',
          '200 gigabyte hard drive'
        ]
      }";
    }

    JsonTextReader reader = new JsonTextReader(new StringReader(strJson));
    
    //Initialize Read
    reader.Read();
    
        if (reader.TokenType == JsonToken.StartArray)
        {
            return JsonConvert.DeserializeObject<List<Object>>(strJson);
        }
        else
        {
            Object media = JsonConvert.DeserializeObject<Object>(strJson);
            return new List<Object>(new[] {media});
        }
}

참고: "개체"는 응답의 Json 속성에 따라 정의해야 합니다.

뉴턴소프트에 대한 마르티네즈와 mfanto의 대답을 설명하다.Newtonsoft와 함께 사용할 수 있습니다.

다음은 목록(및 올바른 이름)이 아닌 배열로 수행하는 예입니다.

public class SingleValueArrayConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject 
            || reader.TokenType == JsonToken.String
            || reader.TokenType == JsonToken.Integer)
        {
            return new T[] { serializer.Deserialize<T>(reader) };
        }
        return serializer.Deserialize<T[]>(reader);
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

그런 다음 속성 위에 다음과 같이 적습니다.

[JsonProperty("INSURANCE")]
[JsonConverter(typeof(SingleValueArrayConverter<InsuranceInfo>))]
public InsuranceInfo[] InsuranceInfo { get; set; }

나머지는 뉴턴소프트가 해줄 거야

return JsonConvert.DeserializeObject<T>(json);

마르티네즈와 엠판토에게 건배!

믿거나 말거나, 이것은 하위 항목과 함께 작동합니다.(필요할 수도 있습니다.) 그래서...다른 오브젝트/어레이 하이브리드가 있는 경우 해당 속성에서 다시 사용합니다.

이를 통해 개체를 json으로 다시 초기화할 수도 있습니다.재시리얼라이즈 할 때는 항상 어레이가 됩니다.

수업은 이렇게 쓰는 게 좋을 것 같은데...!!!

public class FacebookAttachment
    {

        [JsonProperty("attachment")]
        public Attachment Attachment { get; set; }

        [JsonProperty("permalink")]
        public string Permalink { get; set; }
    }

public class Attachment
    {

        [JsonProperty("media")]
        public Media Media { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("caption")]
        public string Caption { get; set; }

        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("properties")]
        public Properties Properties { get; set; }

        [JsonProperty("icon")]
        public string Icon { get; set; }

        [JsonProperty("fb_object_type")]
        public string FbObjectType { get; set; }
    }
 public class Media
    {
    }
 public class Properties
    {
    }

언급URL : https://stackoverflow.com/questions/5224697/deserializing-json-when-sometimes-array-and-sometimes-object