CustomCreationConverter를 사용한 JSON 역 직렬화를 통해 유형 생성 및 데이터 주입

롭 카이트

일부 작업에 정보를 주입해야하는 작업 기반 응용 프로그램이 있습니다. 작업은 복제하거나 저장 파일에 저장할 수 있으며, 각 경우 클래스는 JSON으로 직렬화됩니다. 작업에 전달 된 응용 프로그램 정보는 응용 프로그램 세션 만 유지하기 때문에 저장되지 않습니다.

public interface IApplicationData { }
public class ApplicationData : IApplicationData { }

public interface ITask {
   IApplicationData Data { get; }
}

[DataContract]
public abstract class Task : ITask, ICloneable {
   protected Task(IApplicationData data = null) {
      Data = data;
   }

   public IApplicationData Data { get; }

   public object Clone() {
      var settings = new JsonSerializerSettings() {
         TypeNameHandling = TypeNameHandling.All
      };
      settings.Converters.Add(new TaskCreator(Data));

      var json = JsonConvert.SerializeObject(this, settings);

      // Reflection equivalent of JsonConvert.DeserializeObject<T>(json, settings);
      var expectedParameters = new Type[] { typeof(string), typeof(JsonSerializerSettings) };
      var method = typeof(JsonConvert).GetMethods().Where(mi => mi.IsGenericMethod && mi.IsStatic && mi.IsPublic && mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(expectedParameters)).Single();
      return method.MakeGenericMethod(this.GetType()).Invoke(null, new object[] { json, settings });
   }
}

태스크는 애플리케이션 데이터를 보유하도록 '옵트 인'할 수 있으며 그렇지 않은 경우 아래와 같이 보일 수 있습니다.

public class NoDataTask : Task {
   public NoDataTask() { }
}

public class DataTask : Task {
   public DataTask(IApplicationData data) : base(data) { }
}

나는이 구현 CustomCreationConverterJSON에서 deserialising 때 해당 클래스의 새로운 인스턴스를 생성 (이은에 활용되고 눈치 챘을 수도 Clone()에서 구현 Task위 기본 클래스.

public class TaskCreator : CustomCreationConverter<Task> {
   //public TaskCreator() { } // uncomment to try using converter with JsonProperty attribute in Project

   private readonly IApplicationData _data;
   public TaskCreator(IApplicationData data) {
      _data = data;
   }

   public override Task Create(Type objectType) {
      var hasDataConstructor = objectType.GetConstructor(new Type[] { typeof(IApplicationData) }) != null;
      return hasDataConstructor ? (Task)Activator.CreateInstance(objectType, _data) : (Task)Activator.CreateInstance(objectType);
   }
}

이것은 Clone()메서드에서 필요한대로 정확히 작동 하며 objectType수신 된 DerivedClass ( DataTask아래 예제)

var data = new ApplicationData();
var dataTask = new DataTask(data);
var dataTaskCloneData = ((DataTask)dataTask.Clone()).Data; // still has data intact - excellent

그러나 작업을 저장하는 경우이 작업을 수행하는 방법에 대해 잘 모르겠습니다. 나는 현재 내가 직렬화 / 역 직렬화 Project하는 List<ITask>것을 포함 하는 클래스가 있습니다 . 이는 각 작업의 데이터와 관련하여 완벽하게 작동하지만 ApplicationData역 직렬화 된 작업 인스턴스에 를 삽입 할 수 없습니다 .

[DataContract]
public class Project {
   [DataMember]
   //[JsonProperty(ItemConverterType = typeof(TaskCreator))] // uncomment to force use of converter
   public List<ITask> Tasks { get; set; }
}
var project = new Project {
   Tasks = new List<ITask> {
      new NoDataTask(),
      new DataTask(data)
   }
};

var serialiserSettings = new JsonSerializerSettings {
   TypeNameHandling = TypeNameHandling.All
};
serialiserSettings.Converters.Add(new TaskCreator(data));

var json = JsonConvert.SerializeObject(project, serialiserSettings);
var projectCopy = JsonConvert.DeserializeObject<Project>(json, serialiserSettings);

var projectCopyTask2Data = projectCopy.Tasks[1].Data; // data is null - bad

List<ITask>변환기가 포함 된 프로젝트로 인해 사용되지 않는 것으로 나타났습니다 . 변환기를 추가 할 수 CustomCreationConverter<ITask>있지만 어느 쪽이든 변환기에 objectType전달되는 형식은 항상 유형 ITask이지만 적절한 새 인스턴스를 만들 수 있도록 파생 된 클래스가 필요합니다.

[JsonProperty]속성을 추가하면 변환기가있는 그대로 사용할 수있는 기능이 제공되지만 매개 변수가없는 생성자를 사용하지 않고이를 적용 할 수있는 방법을 알지 못합니다 IApplicationData.

여기 .NET 바이올린 예 - https://dotnetfiddle.net/WdyfDv

롭 카이트

다음과 같이 내 자신의 JsonConverter ( CustomCreationConverterin Newtonsoft.Json.Converters- GitHub 링크 기반)를 작성하여 문제를 해결할 수있었습니다 .

public class TaskCreator : JsonConverter<ITask> {
    private readonly IApplicationData _data;
    public TaskCreator(IApplicationData data) {
        _data = data;
    }

    public override ITask ReadJson(JsonReader reader, Type objectType, [AllowNull] ITask existingValue, bool hasExistingValue, JsonSerializer serializer) {
        if (reader.TokenType == JsonToken.Null) {
            return null;
        }

        // Determine and create the task by reading the type in the JSON
        var jObj = JObject.Load(reader);
        var jsonType = jObj["$type"]?.ToString();
        if (string.IsNullOrWhiteSpace(jsonType)) throw new JsonSerializationException("Cannot determine type of task to create.");
        var type = Type.GetType(jsonType);
        if (type == null) throw new JsonSerializationException($"Could not find the task type {jsonType}");
        var value = Create(type);
        if (value == null) throw new JsonSerializationException("No object created.");

        reader = jObj.CreateReader();
        serializer.Populate(reader, value);
        return value;
    }

    /// <summary>
    /// Creates an object which will then be populated by the serializer.
    /// </summary>
    /// <param name="objectType">Type of the object.</param>
    /// <returns>The created object.</returns>
    public ITask Create(Type objectType) {
        var hasDataConstructor = objectType.GetConstructor(new Type[] { typeof(IApplicationData) }) != null;
        return hasDataConstructor ? (ITask)Activator.CreateInstance(objectType, _data) : (ITask)Activator.CreateInstance(objectType);
    }

    /// <summary>
    /// Writes the JSON representation of the object.
    /// </summary>
    /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
    /// <param name="value">The value.</param>
    /// <param name="serializer">The calling serializer.</param>
    public override void WriteJson(JsonWriter writer, [AllowNull] ITask value, JsonSerializer serializer) {
        throw new NotSupportedException($"{ nameof(TaskCreator) } should only be used while deserializing.");
    }

    /// <summary>
    /// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
    /// </value>
    public override bool CanWrite => false;
}

'마법' ReadJson()은 파생 클래스 ITask가 json '$ type'에서 추출되고 리플렉션을 사용하여 생성 되는 곳에서 발생합니다 . 이렇게하려면 TypeNameHandling이로 설정되어 있어야합니다.이 설정 TypeNameHandling.Objects은 직렬화 기 설정에 있습니다.

이것을 사용하기 위해 클래스 에서 JsonProperty속성을 제거 하고 변환기가 다음과 같이 포함 Project되도록 할 JsonSerializerSettings수 있습니다.

var data = new ApplicationData("Hello World");
var project = new Project {
    Tasks = new List<ITask> {
        new NoDataTask(),
        new DataTask(data)
    }
};
var serialiserSettings = new JsonSerializerSettings {
    TypeNameHandling = TypeNameHandling.All
};
serialiserSettings.Converters.Add(new TaskCreator(data));
var json = JsonConvert.SerializeObject(project, serialiserSettings);
var projectCopy = JsonConvert.DeserializeObject<Project>(json, serialiserSettings);

완전히 동작하는 예제 (.NET 바이올린) 여기 - https://dotnetfiddle.net/Ecrz2S

이 솔루션은 여전히 ​​나에게 약간 '해키'느낌이 들기 때문에 누군가 제안 할 것이 있다면 대체 접근 방식에 대해 여전히 매우 열려 있습니다.

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

Firestore DB를 사용한 Flutter 데이터 직렬화 및 역 직렬화

분류에서Dev

clrmq를 통한 직렬화 및 역 직렬화

분류에서Dev

속성 이름에 $를 사용하여 JSON 역 직렬화

분류에서Dev

@JsonTypeInfo 및 @JsonSubTypes를 사용하여 JSON을 다형성 개체 모델로 역 직렬화하지 않습니까?

분류에서Dev

JAXB 역 직렬화를 통한 개체 생성 사용자 지정

분류에서Dev

JSON에서 /에서 중첩 된 파생 개체를 사용하여 C # 데이터 개체 역 직렬화

분류에서Dev

C #을 사용하여 JSON 데이터를 역 직렬화하려면 어떻게해야합니까?

분류에서Dev

GSON를 사용하여 알 수없는 원시적 인 JSON 속성 유형을 역 직렬화하는 방법

분류에서Dev

C #을 사용하여 XML 파일에서 데이터를 직렬화 및 역 직렬화하는 방법은 무엇입니까?

분류에서Dev

중첩 된 JSON API 데이터를 CSV로 역 직렬화 및 내보내기

분류에서Dev

유형이있는 JSON.NET을 통해 들쭉날쭉 한 배열 역 직렬화

분류에서Dev

ServiceStack의 유형 정보를 사용한 일회성 JSON 직렬화

분류에서Dev

Avro를 사용한 중첩 된 특정 유형 역 직렬화

분류에서Dev

Ext JS 저장소에서 json 데이터를 역 직렬화하는 동안 모델 유형 구별

분류에서Dev

Java 및 Jersey에서 categoryID를 키로 사용하는 범주의 JSON 역 직렬화

분류에서Dev

Jackson의 다형성 직렬화 / 역 직렬화 및 사용자 지정 직렬화 / 역 직렬화

분류에서Dev

Jackson을 사용한 JSON 파일의 다형성 역 직렬화

분류에서Dev

역 직렬화 참조주기는 데이터를 생성자로 가져 오지 않습니다.

분류에서Dev

다양한 유형의 객체를 포함하는 JSON 배열 역 직렬화

분류에서Dev

루프를 통해 유효한 열거 형 데이터 유형 입력 받기

분류에서Dev

MultiThreading C #을 사용하여 직렬 포트를 통해 LOG 화면에 데이터 읽기 및 쓰기

분류에서Dev

C : 직렬화 된 데이터를 유형으로 사용

분류에서Dev

Jackson-주석없이 Builder를 사용하여 역 직렬화

분류에서Dev

Jackson을 사용하여 JAVA 클래스를 대상으로하는 JSON 직렬화 및 역 직렬화

분류에서Dev

Json.NET을 사용하여 ArrayList의 ArrayList를 직렬화 및 역 직렬화하는 방법

분류에서Dev

C # 역 직렬화는 사용자 지정 코드를 통해 특성에 대한 바이트 배열로 문자열을 자동으로 역 직렬화합니까?

분류에서Dev

Gson이 JSON 데이터를 역 직렬화하지 않음

분류에서Dev

Json.NET은 사용자 정의 getter 및 변경 불가능한 유형으로 속성을 역 직렬화하지 않습니다.

분류에서Dev

Json은 인터페이스를 속성으로 사용하여 복잡한 개체를 역 직렬화합니다.

Related 관련 기사

  1. 1

    Firestore DB를 사용한 Flutter 데이터 직렬화 및 역 직렬화

  2. 2

    clrmq를 통한 직렬화 및 역 직렬화

  3. 3

    속성 이름에 $를 사용하여 JSON 역 직렬화

  4. 4

    @JsonTypeInfo 및 @JsonSubTypes를 사용하여 JSON을 다형성 개체 모델로 역 직렬화하지 않습니까?

  5. 5

    JAXB 역 직렬화를 통한 개체 생성 사용자 지정

  6. 6

    JSON에서 /에서 중첩 된 파생 개체를 사용하여 C # 데이터 개체 역 직렬화

  7. 7

    C #을 사용하여 JSON 데이터를 역 직렬화하려면 어떻게해야합니까?

  8. 8

    GSON를 사용하여 알 수없는 원시적 인 JSON 속성 유형을 역 직렬화하는 방법

  9. 9

    C #을 사용하여 XML 파일에서 데이터를 직렬화 및 역 직렬화하는 방법은 무엇입니까?

  10. 10

    중첩 된 JSON API 데이터를 CSV로 역 직렬화 및 내보내기

  11. 11

    유형이있는 JSON.NET을 통해 들쭉날쭉 한 배열 역 직렬화

  12. 12

    ServiceStack의 유형 정보를 사용한 일회성 JSON 직렬화

  13. 13

    Avro를 사용한 중첩 된 특정 유형 역 직렬화

  14. 14

    Ext JS 저장소에서 json 데이터를 역 직렬화하는 동안 모델 유형 구별

  15. 15

    Java 및 Jersey에서 categoryID를 키로 사용하는 범주의 JSON 역 직렬화

  16. 16

    Jackson의 다형성 직렬화 / 역 직렬화 및 사용자 지정 직렬화 / 역 직렬화

  17. 17

    Jackson을 사용한 JSON 파일의 다형성 역 직렬화

  18. 18

    역 직렬화 참조주기는 데이터를 생성자로 가져 오지 않습니다.

  19. 19

    다양한 유형의 객체를 포함하는 JSON 배열 역 직렬화

  20. 20

    루프를 통해 유효한 열거 형 데이터 유형 입력 받기

  21. 21

    MultiThreading C #을 사용하여 직렬 포트를 통해 LOG 화면에 데이터 읽기 및 쓰기

  22. 22

    C : 직렬화 된 데이터를 유형으로 사용

  23. 23

    Jackson-주석없이 Builder를 사용하여 역 직렬화

  24. 24

    Jackson을 사용하여 JAVA 클래스를 대상으로하는 JSON 직렬화 및 역 직렬화

  25. 25

    Json.NET을 사용하여 ArrayList의 ArrayList를 직렬화 및 역 직렬화하는 방법

  26. 26

    C # 역 직렬화는 사용자 지정 코드를 통해 특성에 대한 바이트 배열로 문자열을 자동으로 역 직렬화합니까?

  27. 27

    Gson이 JSON 데이터를 역 직렬화하지 않음

  28. 28

    Json.NET은 사용자 정의 getter 및 변경 불가능한 유형으로 속성을 역 직렬화하지 않습니다.

  29. 29

    Json은 인터페이스를 속성으로 사용하여 복잡한 개체를 역 직렬화합니다.

뜨겁다태그

보관