private IProtoTypeSerializer BuildSerializer(bool isRoot) { // reference tracking decorators (RootDecorator, NetObjectDecorator, NetObjectValueDecorator) // should always be applied only one time (otherwise will consider new objects as already written): // #1 For collection types references are handled either by RootDecorator or // by ValueMember which owns the value (so outside of this scope) // because the value is treated as single object // #2 For members: ordinal ValueMembers are used and they will handle references when appropriate if (Helpers.IsEnum(Type)) { Debug.Assert(IsSimpleValue); IProtoTypeSerializer ser = new WireTypeDecorator(WireType.Variant, new EnumSerializer(Type, GetEnumMap())); if (isRoot && !GetRootStartsGroup()) { ser = new RootFieldNumberDecorator(ser, ListHelpers.FieldItem); } return(ser); } Type itemType = _settingsValueFinal.Member.Collection.ItemType; if (itemType != null) { if (_surrogate != null) { throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot use a surrogate"); } Type defaultType = null; ResolveListTypes(_model, Type, ref itemType, ref defaultType); if (_fields.Count != 0) { throw new ArgumentException("Repeated data (an array, list, etc) has inbuilt behavior and can't have fields"); } // apply default member settings to type settings too var s = _settingsValueFinal.Member; // but change this: s.EffectiveType = Type; // not merged with anything so assign s.Collection.ConcreteType = _settingsValueFinal.ConstructType ?? defaultType; s.Collection.Append = false; // allowed only on members s.WriteAsDynamicType = false; // allowed only on members // this should be handled as collection if (s.Collection.ItemType == null) { s.Collection.ItemType = itemType; } var vs = _rootNestedVs.Clone(); vs.SetSettings(s, 0); vs.DefaultLevel = new ValueSerializationSettings.LevelValue(s.MakeDefaultNestedLevel()); WireType wt; var ser = (IProtoTypeSerializer)_model.ValueSerializerBuilder.BuildValueFinalSerializer(vs, false, out wt); // standard root decorator won't start any field // in compatibility mode collections won't start subitems like normally // so wrap with field if (isRoot && !GetRootStartsGroup()) { ser = new RootFieldNumberDecorator(ser, TypeModel.EnumRootTag); } return(ser); } if (BaseType != null && !BaseType.GetFinalSettingsCopy().IgnoreListHandling&& RuntimeTypeModel.CheckTypeIsCollection(_model, BaseType.Type)) { throw new ArgumentException("A subclass of a repeated data (an array, list, etc should be handled too as a collection"); } // #2 if (_surrogate != null) { MetaType mt = _model[_surrogate], mtBase; while ((mtBase = mt.BaseType) != null) { mt = mtBase; } return(new SurrogateSerializer(_model, Type, _surrogate, mt.Serializer)); } if (_settingsValueFinal.IsAutoTuple) { if (_tupleCtor == null) { throw new InvalidOperationException("Can't find tuple constructor"); } return(new TupleSerializer(_model, _tupleCtor, _tupleFields.ToArray(), _settingsValueFinal.PrefixLength.GetValueOrDefault(true))); } var fields = new BasicList(_fields.Cast <object>()); fields.Trim(); int fieldCount = fields.Count; int subTypeCount = _subTypes?.Count ?? 0; int[] fieldNumbers = new int[fieldCount + subTypeCount]; IProtoSerializerWithWireType[] serializers = new IProtoSerializerWithWireType[fieldCount + subTypeCount]; int i = 0; if (subTypeCount != 0) { Debug.Assert(_subTypes != null, "_subTypes != null"); foreach (SubType subType in _subTypes) { #if WINRT if (!subType.DerivedType.IgnoreListHandling && ienumerable.IsAssignableFrom(subType.DerivedType.Type.GetTypeInfo())) #else if (!subType.DerivedType.IgnoreListHandling && _model.MapType(ienumerable).IsAssignableFrom(subType.DerivedType.Type)) #endif { throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a subclass"); } fieldNumbers[i] = subType.FieldNumber; serializers[i++] = subType.GetSerializer(_model); } } if (fieldCount != 0) { foreach (ValueMember member in fields) { fieldNumbers[i] = member.FieldNumber; serializers[i++] = member.Serializer; } } BasicList baseCtorCallbacks = null; MetaType tmp = BaseType; while (tmp != null) { MethodInfo method = tmp.HasCallbacks ? tmp.Callbacks.BeforeDeserialize : null; if (method != null) { if (baseCtorCallbacks == null) { baseCtorCallbacks = new BasicList(); } baseCtorCallbacks.Add(method); } tmp = tmp.BaseType; } MethodInfo[] arr = null; if (baseCtorCallbacks != null) { arr = new MethodInfo[baseCtorCallbacks.Count]; baseCtorCallbacks.CopyTo(arr, 0); Array.Reverse(arr); } // root serializer should be always from base type Debug.Assert(!OmitTypeSearchForRootSerialization); if (isRoot && BaseType != null) { return(new ForbiddenRootStub(Type)); } return(new TypeSerializer(_model, Type, fieldNumbers, serializers, arr, BaseType == null, !_settingsValueFinal.SkipConstructor, _callbacks, _settingsValueFinal.ConstructType, _factory, _settingsValueFinal.PrefixLength.Value)); }
public IProtoSerializerWithWireType TryGetCoreSerializer(BinaryDataFormat dataFormat, Type type, out WireType defaultWireType, ref ValueFormat format, bool dynamicType, bool appendCollection, bool isPackedCollection, bool allowComplexTypes, ref object defaultValue) { if (format == ValueFormat.NotSpecified) { throw new ArgumentException("Format should be specified for TryGetCoreSerializer", nameof(format)); } if (format != ValueFormat.Compact && _model.ProtoCompatibility.SuppressValueEnhancedFormat) { throw new InvalidOperationException("TryGetCoreSerializer should receive final format with ProtoCompatibility already taken into account"); } Type originalType = type; #if !NO_GENERICS { Type tmp = Helpers.GetNullableUnderlyingType(type); if (tmp != null) { type = tmp; } } #endif defaultWireType = WireType.None; IProtoSerializerWithWireType ser = null; if (Helpers.IsEnum(type)) { if (allowComplexTypes && _model != null) { // need to do this before checking the typecode; an int enum will report Int32 etc defaultWireType = WireType.Variant; ser = new WireTypeDecorator(defaultWireType, new EnumSerializer(type, _model.GetEnumMap(type))); } else { // enum is fine for adding as a meta-type defaultWireType = WireType.None; return(null); } } if (ser == null) { ser = TryGetBasicTypeSerializer(dataFormat, type, out defaultWireType, !appendCollection); if (ser != null && Helpers.GetTypeCode(type) == ProtoTypeCode.Uri) { // should be after uri but uri should always be before collection if (defaultValue != null) { ser = new DefaultValueDecorator(_model, defaultValue, ser); defaultValue = null; } } } if (ser == null) { var parseable = _model.AllowParseableTypes ? ParseableSerializer.TryCreate(type, _model) : null; if (parseable != null) { defaultWireType = WireType.String; ser = new WireTypeDecorator(defaultWireType, parseable); } } if (ser != null) { return((isPackedCollection || !allowComplexTypes) ? ser : DecorateValueSerializer(originalType, dynamicType ? dataFormat : (BinaryDataFormat?)null, ref format, ser)); } if (allowComplexTypes) { int baseKey = _model.GetKey(type, false, true); defaultWireType = WireType.StartGroup; if (baseKey >= 0 || dynamicType) { if (dynamicType) { return(new NetObjectValueDecorator(originalType, format == ValueFormat.Reference || format == ValueFormat.LateReference, dataFormat, !_model.ProtoCompatibility.SuppressNullWireType, _model)); } else if (format == ValueFormat.LateReference && CanTypeBeAsLateReferenceOnBuildStage(baseKey, _model)) { return(new NetObjectValueDecorator(originalType, baseKey, true, true, _model[type], !_model.ProtoCompatibility.SuppressNullWireType, _model)); } else if (MetaType.IsNetObjectValueDecoratorNecessary(_model, format)) { return(new NetObjectValueDecorator(originalType, baseKey, format == ValueFormat.Reference || format == ValueFormat.LateReference, false, _model[type], !_model.ProtoCompatibility.SuppressNullWireType, _model)); } else { return(new ModelTypeSerializer(type, baseKey, _model[type], _model)); } } } defaultWireType = WireType.None; return(null); }