Ejemplo n.º 1
0
        /// <summary>
        /// Accepts the GeoJson coordinates property, and iterates the top level array to determine
        /// which of the four schemas the coordinates match.
        /// </summary>
        /// <param name="reader">A Utf8JsonReader positioned at the GeoJson coordinates property.</param>
        /// <returns>A reader which will parse out correct parser based on the schema detected.</returns>
        /// <exception cref="JsonException"></exception>
        public static GeoJsonCoordinateReader Create(ref Utf8JsonReader reader)
        {
            GeoJsonCoordinateReader result;

            reader.Expect(JsonTokenType.StartArray);

            int detectedLevel = 0;

            // How many nested StartArray tokens we find identifies which of the four schemas we're dealing with
            for (; reader.Read() && reader.TokenType == JsonTokenType.StartArray; detectedLevel++)
            {
                if (detectedLevel == 4)
                {
                    throw new JsonException($"Deserialization failed. GeoJson property '{GeoJsonConstants.CoordinatesPropertyName}' does not contain recognizable GeoJson.");
                }
            }

            reader.Expect(JsonTokenType.Number);

            switch (detectedLevel)
            {
            case 0:
                result = new LevelZeroGeoJsonCoordinateReader();
                break;

            case 1:
                result = new LevelOneGeoJsonCoordinateReader();
                break;

            case 2:
                result = new LevelTwoGeoJsonCoordinateReader();
                break;

            default:
                result = new LevelThreeGeoJsonCoordinateReader();
                break;
            }

            result.Process(ref reader);

            return(result);
        }
        /// <summary>
        /// Called when a GeometryCollection is either the top level type, or when a GeometryCollection has been nested inside
        /// another GeometryCollection (which is legal, but apparently discouraged.)
        /// </summary>
        /// <param name="reader">A Utf8JsonReader positioned at the beginning of the geometries array.</param>
        /// <returns>The list of Geographies which were extracted.</returns>
        private List <Geography> ProcessGeometryCollection(ref Utf8JsonReader reader)
        {
            List <Geography> result = new List <Geography>();

            reader.Expect(JsonTokenType.StartArray);

            while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
            {
                Geography geography = ProcessSingleGeography(ref reader);

                result.Add(geography);
            }

            return(result);
        }
        /// <summary>
        /// Some GeoJson documents define a single geography, e.g. Point and MultiPolygon. GeometryCollection on the other
        /// hand contains a list of Geographies specified not in the coordinates property, but in the geometries property.
        /// This methodwill be called at the top level by Read(), and in the case of a GeometryCollection, once for every
        /// entry in its geometries array.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        /// <exception cref="JsonException"></exception>
        private Geography ProcessSingleGeography(ref Utf8JsonReader reader)
        {
            string type = null;
            GeoJsonCoordinateReader geoJsonCoordinateReader = null;
            List <Geography>        geographies             = null;

            reader.Expect(JsonTokenType.StartObject);

            while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
            {
                reader.Expect(JsonTokenType.PropertyName);

                string propertyName = reader.GetString();

                reader.Read();

                if (string.Equals(GeoJsonConstants.TypePropertyName, propertyName, StringComparison.Ordinal))
                {
                    reader.Expect(JsonTokenType.String);
                    type = reader.GetString();
                }

                else if (string.Equals(GeoJsonConstants.CoordinatesPropertyName, propertyName, StringComparison.Ordinal))
                {
                    geoJsonCoordinateReader = GeoJsonCoordinateReader.Create(ref reader);
                }

                else if (string.Equals(GeoJsonConstants.GeometriesPropertyName, propertyName, StringComparison.Ordinal))
                {
                    geographies = ProcessGeometryCollection(ref reader);
                }

                else
                {
                    reader.Skip();
                }
            }

            if (type == null)
            {
                throw new JsonException($"Deserialization failed. Required GeoJson property '{GeoJsonConstants.TypePropertyName}' not found.");
            }

            if (!TypeNames.Contains(type))
            {
                string valid = TypeNames.FirstOrDefault(z => z.Equals(type, StringComparison.OrdinalIgnoreCase));

                if (valid != null)
                {
                    throw new JsonException($"Deserialization failed. GeoJson property '{GeoJsonConstants.TypePropertyName}' values are case sensitive. Use '{valid}' instead.");
                }

                throw new JsonException($"Deserialization failed. GeoJson property '{GeoJsonConstants.TypePropertyName}' contains an invalid value: '{type}'.");
            }

            if (geoJsonCoordinateReader != null)
            {
                return(geoJsonCoordinateReader.GetGeography(type));
            }

            else if (geographies != null)
            {
                return(GeographyFactory.Collection().Create(geographies));
            }

            else
            {
                if (type == GeoJsonConstants.GeometryCollectionTypeName)
                {
                    throw new JsonException($"Deserialization failed. Required GeoJson property '{GeoJsonConstants.GeometriesPropertyName}' not found.");
                }

                else
                {
                    throw new JsonException($"Deserialization failed. Required GeoJson property '{GeoJsonConstants.CoordinatesPropertyName}' not found.");
                }
            }
        }
        /// <inheritdoc/>
        public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.Null)
            {
                return(null);
            }

            string type      = default;
            double?longitude = default;
            double?latitude  = default;

            reader.Expect(JsonTokenType.StartObject);
            while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
            {
                reader.Expect(JsonTokenType.PropertyName);
                string propertyName = reader.GetString();

                reader.Read();
                if (string.Equals(TypePropertyName, propertyName, StringComparison.Ordinal))
                {
                    reader.Expect(JsonTokenType.String);
                    type = reader.GetString();
                }
                else if (string.Equals(CoordinatesPropertyName, propertyName, StringComparison.Ordinal))
                {
                    reader.Expect(JsonTokenType.StartArray);

                    // Longitude
                    reader.Read();
                    reader.Expect(JsonTokenType.Number);
                    longitude = reader.GetDouble();

                    // Latitude
                    reader.Read();
                    reader.Expect(JsonTokenType.Number);
                    latitude = reader.GetDouble();

                    // Skip the rest.
                    do
                    {
                        reader.Read();
                    } while (reader.TokenType != JsonTokenType.EndArray);
                }
                else
                {
                    reader.Skip();
                }
            }

            if (!string.Equals(PointTypeName, type, StringComparison.Ordinal))
            {
                throw new JsonException($"Deserialization of {nameof(GeographyPoint)} failed. Expected geographic type: '{PointTypeName}'.");
            }

            if (!longitude.HasValue || !latitude.HasValue)
            {
                throw new JsonException($"Deserialization of {nameof(GeographyPoint)} failed. Expected both longitude and latitude.");
            }

            return(GeographyPoint.Create(latitude.Value, longitude.Value));
        }