2014-10-02 3 views
1

그래서 사용자가 다른 serialize를 구현하고 선택할 수 있도록 Unity3D 용 사용자 지정 serialization 시스템을 작성하고 있습니다. 현재 BinaryFormatterprotobuf-net을 지원합니다. 하나; 이 시스템에서 나는 내 시리얼 라이저와 함께 멋진 연주하고 싶습니다 직렬화에 대한 지속적인 사용자 정의 규칙을 가지고 : 직렬화하지 않습니다 그들은 부작용 [Serializable] protobuf-net 사용자 지정 serialization 및 모델 구성

  • 속성 주석 경우에만

    1. 유형 직렬화하고, 단지 자동차들
    2. 공공 분야/자동 속성을 암시 적으로 직렬화 사용자 정의 속성이 (내가 사람들의 다양한이 그들에 적용 할 때
    3. 비 공공 분야/자동 속성은 직렬화 : [Save], [Serialize][SerializeField]
    4. 01 내가 protobufs의 정의 ProtoContract 같은 속성을 사용하지 않도록 23,516,

    지금은 등 ProtoMember

    ,이 규칙에 적응하는 내 protobuf - 네트 모델을 적응하기 위해 내가 상상하는 방법을 싶습니다 사용자가 자신의 커스텀 타입을 추가 할 수있는 직렬화 가능한 타입의 배열을 가지는 것입니다. (그런 식으로 그 타입에 대해 ProtoContract를 필요로하지 않습니다.) 그런 타입을 반복하여 내 모델에 추가 할 것입니다. Foreach 유형, 나는 내 직렬화 규칙을 만족시키는 멤버를 얻고이를 모델에 추가한다.

    내가하고 싶은 또 다른 것은, 당신은 사용자가 단지 A를 추가, 명시 적으로 BC을 추가 할 필요가 없습니다 내가 A의 아이를 얻을 것입니다 어린이 BC와 추상 클래스 A을 가지고 말하는 것입니다 그들을 직접 추가하십시오.

    다음
    [Serializble] 
    public abstract class AbstractBase 
    { 
        public abstract int Num { get; set; } 
    } 
    
    [Serializble] 
    public class Child1 : AbstractBase 
    { 
        public int x; 
    
        public override int Num { get { return x; } set { x = value; } } 
    } 
    
    [Serializble] 
    public class Child2 : AbstractBase 
    { 
        public int y; 
        public int z; 
    
        public override int Num { get { return y; } set { y = value; } } 
    } 
    
    // ProtobufSerializableTypes.cs 
    public static Type[] SerializableTypes = new[] 
    { 
        typeof(AbstractBase) 
    }; 
    

    해봤 내용은 다음과 같습니다 :

    [ProtoContract] 
    [ProtoInclude(1, typeof(Child1))] 
    [ProtoInclude(2, typeof(Child2))] 
    public abstract class AbstractBase 
    { 
        public abstract int Num { get; set; } 
    } 
    
    [ProtoContract] 
    public class Child1 : AbstractBase 
    { 
        [ProtoMember(1)] 
        public int x; 
    
        public override int Num { get { return x; } set { x = value; } } 
    } 
    
    [ProtoContract] 
    public class Child2 : AbstractBase 
    { 
        [ProtoMember(1)] 
        public int y; 
        [ProtoMember(2)] 
        public int z; 
    
        public override int Num { get { return y; } set { y = value; } } 
    } 
    

    내가 그들을이 쓸 수 있도록하고 싶습니다 대신 사용자가이 작성해야하는 :

    내 질문에 귀결

    [TestClass] 
    public class ProtobufDynamicSerializationTestSuite 
    { 
        private AbstractBase Base { get; set; } 
        private Type[] SerializableTypes { get; set; } 
    
        [TestInitialize] 
        public void Setup() 
        { 
         Base = new Child1(); 
         SerializableTypes = new[] 
         { 
          typeof(AbstractBase) 
         }; 
        } 
    
        [TestMethod] 
        public void ShouldCopyWithCustomConfig() 
        { 
         var model = TypeModel.Create(); 
    
         Func<Type, MetaType> addType = type => 
         { 
          log("adding type: {0}", type.Name); 
          return model.Add(type, false); 
         }; 
    
         var hierarchy = new Dictionary<MetaType, List<Type>>(); 
         for (int i = 0; i < SerializableTypes.Length; i++) 
         { 
          var type = SerializableTypes[i]; 
          var meta = addType(type); 
          var temp = new List<Type>(); 
          var children = type.Assembly.GetTypes().Where(t => t.IsSubclassOf(type) && !t.IsAbstract).ToList(); 
          for(int j = 0; j < children.Count; j++) 
          { 
           var child = children[j]; 
           addType(child); 
           log("adding subtype {0} with id {1}", child.Name, j + 1); 
           meta.AddSubType(j + 1, child); 
           temp.Add(child); 
          } 
          hierarchy[meta] = temp; 
         } 
    
         Func<Type, string[]> getMemberNames = x => 
          //SerializationLogic.GetSerializableMembers(x, null) // real logic 
           x.GetMembers(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) // dummy logic 
              .Where(m => m.MemberType == MemberTypes.Field) 
              .Select(m => m.Name) 
              .ToArray(); 
    
         foreach (var entry in hierarchy) 
         { 
          int id = 1; 
          foreach (var type in entry.Value) 
          { 
           foreach (var member in getMemberNames(type)) 
           { 
            log("adding member {0} to type {1} with id {2}", member, type.Name, id); 
            entry.Key.Add(id++, member); 
           } 
          } 
         } 
    
         Base.Num = 10; 
         var copy = (AbstractBase)model.DeepClone(Base); 
         Assert.AreEqual(copy.Num, 10); 
        } 
    
        void log(string msg, params object[] args) 
        { 
         Console.WriteLine(string.Format(msg, args)); 
        } 
    
        void log(string msg) 
        { 
         log(msg, new object[0]); 
        } 
    } 
    

    그래서 모든 필요한 유형을 모델에 추가하고 모든 하위 유형을 상위 유형에 추가했습니다. 의 다음 추가 된 모든 유형의 반복과 모델에 추가이가 그러나 실패 유형

    에서 (내 직렬화 규칙에 해당) 해당 필드/속성 :

    Test Name: ShouldCopyWithCustomConfig 
    Test Outcome: Failed 
    Result Message: 
    Test method ProtobufTests.ProtobufDynamicSerializationTestSuite.ShouldCopyWithCustomConfig threw exception: 
    System.ArgumentException: Unable to determine member: x 
    Parameter name: memberName 
    Result StandardOutput: 
    adding type: AbstractBase 
    adding type: Child1 
    adding subtype Child1 with id 1 
    adding type: Child2 
    adding subtype Child2 with id 2 
    adding member x to type Child1 with id 1 
    

    어떻게 생각 잘못하고있는거야? 이렇게하는 더 좋은 방법이 있습니까?

    감사합니다.A가 있고, 종류 AB, 처음에 나는이 사전 단계가 없었


    참고, 내가 바로 모델에 추가 한 후 유형의 구성원을 추가했지만, 내가 말할 경우이 실패 B 참조, 형식 A 및 그 구성원을 추가하려고하면 B을 따라 올 테지만 protobuf는이 단계에서 식별 할 수 없습니다. 아직 모델에 추가되지 않았기 때문에 ... 그래서 추가 할 필요가 있다고 생각했습니다. 먼저 유형을 입력 한 다음 구성원을 입력하십시오.

  • +0

    안녕하세요; 나는 protobuf-net의 저자 다. 오늘 하루 종일 Redis 워크숍에 출근 할 수 없으므로 최대한 빨리 할 것입니다. –

    +0

    알려 주셔서 고마워요! 나는 당신의 대답을 기다리고 싶었습니다 : D 나는 당신이 나를 때리는 동안 내 자신의 것을 시도 할 것입니다. – vexe

    +0

    @MarcGravell은 여전히 ​​그것을 알아 내지 못했습니다. 약간의 도움을 사용할 수 있습니다; P – vexe

    답변

    1

    주요 문제는 기본 유형 인 entry.Key이 기본 유형을 나타내는 것으로 보입니다. 특정 하위 유형의 구성원을 설명하려고 시도합니다. 여기에 내가 무슨 짓을했는지 :

    .OrderBy(m => m.Name) // in getMemberNames 
    

    .OrderBy(x => x.FullName) // in var children = 
    

    이 식별자는 적어도 예측 가능한 지 확인하기 :

     foreach (var entry in hierarchy) 
         { 
          foreach (var type in entry.Value) 
          { 
           var meta = model.Add(type, false); 
           var members = getMemberNames(type); 
           log("adding members {0} to type {1}", 
            string.Join(",", members), type.Name); 
           meta.Add(getMemberNames(type)); 
          } 
         } 
    

    내가 또한 몇 가지 엄격한 주문을 추가했다. protobuf에서 id는 매우 중요합니다. 즉, id를 엄격하게 정의하지 않은 결과로 누군가가 모델에 AardvarkCount을 추가하면 모든 ID를 상쇄하고 기존 데이터의 직렬화를 해제 할 수 있습니다. 조심해야 할 것이있다.

    +0

    답변 해 주셔서 감사합니다! - 미안하지만 형식을 모델에 다시 추가하는 것 같습니다. 즉 위의 for 루프에서 얻은 동일한 계층 사전입니다. .. "AardvarkCount"로 무엇을 의미합니까? : – vexe

    +0

    @vexe Add는 기존 MetaType이 이미 정의 된 경우이를 반환합니다. AardvarkCount에 의해, 나는 미래에 반복 가능한 ID 문제를 강조하려고 노력하고있다. 누군가가 유형이나 속성을 추가/제거하는 경우에도 모델이 이전 데이터를 deserialize 할 수 있습니까? protobuf에있는 id는 반복적으로 작동해야합니다. ID가 알파벳순으로 만 생성되는 경우 보증하기 어렵습니다. –

    +0

    고맙습니다! - 그러나; 내가하고있는 일이 과잉이라고 느껴진다. 더 간단한 방법이 있습니까? 모델에 형식을 추가 한 후에 형식 멤버를 추가해야한다고 생각했습니다 (추가 된 마지막 질문을 참고하십시오). 맞습니까? – vexe

    관련 문제