Skip to content

Feature.Id only supports String type #35

Open

Description

@dmitry-kandiner

Hello!

According to https://datatracker.ietf.org/doc/html/rfc7946#section-3.2, the Id field of the Feature, if present, can be either number or string:

If a Feature has a commonly used identifier, that identifier
SHOULD be included as a member of the Feature object with the name
"id", and the value of this member is either a JSON string or
number.

However, current implementation only supports deserialization of string values for this field.
Proposed solutions:

  • A custom JSON converter for this field, with storing the value as a string.
  • Storing the value as a discriminated union of string and int (or long).

Best regards,
Dmitry

Activity

  1. xfischer commented on Aug 6, 2025

    @xfischer
    Member

    Hi @dmitry-kandiner thanks for reporting this. I think the latter option is good, but is not in the language yet (please advise if I'm wrong).

    I might have also a proposal: make the Feature generic (like Feature : Feature) where the Id field type could be specified. That's good when you know what you expect.

    Other (ugly but OK type-wise) idea : let the Feature's Id property be an object and support deserialization from numeric of string.

  2. dmitry-kandiner commented on Aug 6, 2025

    @dmitry-kandiner
    Author

    Hello @xfischer !

    You are right, the discriminated union type is not supported by the language yet, but it can be implemented for this specific need as an internal class for this project.

    Making the Feature class a generic could be fine, but then an array of Features will be limited to a single specification only.

    Letting the Id be an object is the easiest way to implement it, but it will require validation during deserialization and an explicit cast when the field is accessed.

  3. xfischer commented on Aug 6, 2025

    @xfischer
    Member

    You are right, the discriminated union type is not supported by the language yet, but it can be implemented for this specific need as an internal class for this project.

    I wonder how that could be possible. Can you elaborate?

    Making the Feature class a generic could be fine, but then an array of Features will be limited to a single specification only.

    Yes you are right!

    Letting the Id be an object is the easiest way to implement it, but it will require validation during deserialization and an explicit cast when the field is accessed.

    Yes that's a bit old school I don't like it either.

  4. dmitry-kandiner commented on Aug 6, 2025

    @dmitry-kandiner
    Author

    Consider this code snippet:

    public sealed class FeatureId : IEquatable<FeatureId>
    {
        private readonly string _strId;
        private readonly ulong? _numId;
    
        private FeatureId(string str, ulong? num) => (_strId, _numId) = (str, num);
    
        public static implicit operator FeatureId(string str) => new(str, null);
        public static implicit operator FeatureId(ulong num) => new(null, num);
    
        public bool IsNumeric => _numId.HasValue;
        public bool IsString => !IsNumeric;
    
        public static implicit operator string(FeatureId id) => id.IsString ? id._strId : throw new InvalidCastException();
        public static implicit operator ulong(FeatureId id) => id.IsNumeric ? id._numId.Value : throw new InvalidCastException();
    
        public override int GetHashCode() => (_strId.GetHashCode() * 397) ^ _numId.GetHashCode();
    
        public bool Equals(FeatureId other) => _strId == other._strId && _numId == other._numId;
    
        public override bool Equals(object obj) => obj is FeatureId id && Equals(id);
    
        public static bool operator ==(FeatureId left, FeatureId right) => left.Equals(right);
    
        public static bool operator !=(FeatureId left, FeatureId right) => !(left == right);
    }

    I made some basic checks for this code - used it as a type of Feature<TGeometry, TProps>._id. It passes the unit tests you have (not tested with numeric values yet).
    What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions