/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Driver.GeoJsonObjectModel.Serializers { /// /// Represents a serializer helper for GeoJsonObjects. /// /// The type of the coordinates. public class GeoJsonObjectSerializerHelper : SerializerHelper where TCoordinates : GeoJsonCoordinates { // private constants private static class Flags { public const long Type = 1; public const long CoordinateReferenceSystem = 2; public const long BoundingBox = 4; public const long ExtraMember = 8; } // private fields private readonly IBsonSerializer> _boundingBoxSerializer = BsonSerializer.LookupSerializer>(); private readonly IBsonSerializer _coordinateReferenceSystemSerializer = BsonSerializer.LookupSerializer(); private readonly string _type; // constructors /// /// Initializes a new instance of the class. /// /// The type. /// The derived members. public GeoJsonObjectSerializerHelper(string type, params SerializerHelper.Member[] derivedMembers) : base(CreateCombinedMembers(derivedMembers)) { _type = type; } // private static methods private static IEnumerable CreateBaseMembers() { return new[] { new SerializerHelper.Member("type", Flags.Type), new SerializerHelper.Member("bbox", Flags.BoundingBox, isOptional: true), new SerializerHelper.Member("crs", Flags.CoordinateReferenceSystem, isOptional: true), new SerializerHelper.Member("*", Flags.ExtraMember, isOptional: true) }; } private static SerializerHelper.Member[] CreateCombinedMembers(IEnumerable derivedMembers) { var combinedMembers = CreateBaseMembers().Concat(derivedMembers).ToArray(); ThrowIfDuplicateMemberFlags(combinedMembers); return combinedMembers; } private static void ThrowIfDuplicateMemberFlags(SerializerHelper.Member[] members) { var distinctFlags = new HashSet(members.Select(m => m.Flag)); if (distinctFlags.Count < members.Length) { throw new BsonInternalException("Duplicate GeoJsonObject member flags."); } } // public methods /// /// Deserializes a base member. /// /// The context. /// The element name. /// The flag. /// The arguments. public void DeserializeBaseMember(BsonDeserializationContext context, string elementName, long flag, GeoJsonObjectArgs args) { switch (flag) { case Flags.Type: EnsureTypeIsValid(context); break; case Flags.CoordinateReferenceSystem: args.CoordinateReferenceSystem = DeserializeCoordinateReferenceSystem(context); break; case Flags.BoundingBox: args.BoundingBox = DeserializeBoundingBox(context); break; case Flags.ExtraMember: DeserializeExtraMember(context, elementName, args); break; default: throw new BsonInternalException(); } } /// /// Serializes the members. /// /// The type of the value. /// The context. /// The value. /// The delegate to serialize the derived members. public void SerializeMembers(BsonSerializationContext context, TValue value, Action serializeDerivedMembers) where TValue : GeoJsonObject { context.Writer.WriteStartDocument(); SerializeType(context, value.Type); SerializeCoordinateReferenceSystem(context, value.CoordinateReferenceSystem); SerializeBoundingBox(context, value.BoundingBox); serializeDerivedMembers(context, value); SerializeExtraMembers(context, value.ExtraMembers); context.Writer.WriteEndDocument(); } // private methods private GeoJsonBoundingBox DeserializeBoundingBox(BsonDeserializationContext context) { return _boundingBoxSerializer.Deserialize(context); } private GeoJsonCoordinateReferenceSystem DeserializeCoordinateReferenceSystem(BsonDeserializationContext context) { return _coordinateReferenceSystemSerializer.Deserialize(context); } private void DeserializeExtraMember(BsonDeserializationContext context, string elementName, GeoJsonObjectArgs args) { var value = BsonValueSerializer.Instance.Deserialize(context); if (args.ExtraMembers == null) { args.ExtraMembers = new BsonDocument(); } args.ExtraMembers[elementName] = value; } private void EnsureTypeIsValid(BsonDeserializationContext context) { var type = context.Reader.ReadString(); if (type != _type) { throw new FormatException(string.Format( "Invalid GeoJson type: '{0}'. Expected: '{1}'.", type, _type)); } } private void SerializeBoundingBox(BsonSerializationContext context, GeoJsonBoundingBox boundingBox) { if (boundingBox != null) { context.Writer.WriteName("bbox"); _boundingBoxSerializer.Serialize(context, boundingBox); } } private void SerializeCoordinateReferenceSystem(BsonSerializationContext context, GeoJsonCoordinateReferenceSystem coordinateReferenceSystem) { if (coordinateReferenceSystem != null) { context.Writer.WriteName("crs"); _coordinateReferenceSystemSerializer.Serialize(context, coordinateReferenceSystem); } } private void SerializeExtraMembers(BsonSerializationContext context, BsonDocument value) { if (value != null) { foreach (var element in value) { context.Writer.WriteName(element.Name); BsonValueSerializer.Instance.Serialize(context, element.Value); } } } private void SerializeType(BsonSerializationContext context, GeoJsonObjectType type) { context.Writer.WriteString("type", type.ToString()); } } }