Vínculos directos de URI coincidentes

Para hacer coincidir URIs jerárquicos con patrones y extraer argumentos, usa UriDeepLinkMatcher. Se basa en kotlinx.serialization para deserializar los argumentos coincidentes en tus clases clave.

Para crear un UriDeepLinkMatcher, proporciona un patrón DeepLinkUri y el serializador para la clave correspondiente:

Para URIs no jerárquicos o esquemas personalizados (como tel:), consulta Cómo crear comparadores de vínculos directos personalizados.

Patrones de coincidencia compatibles

UriDeepLinkMatcher hace coincidir los URIs en función de sus cinco componentes: esquema, autoridad, ruta de acceso, consulta y fragmento. En las siguientes secciones, se describen la sintaxis de patrones admitida, los marcadores de posición de argumentos y las reglas de coincidencia para cada componente.

Coincidencia de esquemas

Si no hay un esquema presente en el patrón de URI, coinciden http y https. Para que coincida con un esquema específico, inclúyelo en el patrón. Como excepción, un http esquema en un patrón coincide con los URIs de solicitud http y https, mientras que https en un patrón solo coincide con las solicitudes https.

URI de patrón URI de solicitud Coincidencia
www.example.com https://www.example.com
www.example.com http://www.example.com
http://www.example.com http://www.example.com
http://www.example.com https://www.example.com
https://www.example.com http://www.example.com
myapp://www.example.com myapp://www.example.com

Coincidencia de autoridad

UriDeepLinkMatcher realiza una coincidencia exacta que no distingue mayúsculas de minúsculas en la autoridad del URI (host y puerto opcional). No se admiten marcadores de posición ni comodines en la autoridad, y no se extraen argumentos:

URI de patrón URI de solicitud Coincidencia
example.com https://example.com
example.com https://EXAMPLE.COM
example.com https://sub.example.com
example.com https://www.example.com
example.com https://example.com:8080
example.com:8080 https://example.com:8080
example.com:8080 https://example.com

Coincidencia de ruta

Se admiten los siguientes patrones de ruta:

URI de patrón URI de solicitud Coincidencia Argumentos extraídos
www.example.com/users https://www.example.com/users Ninguno
www.example.com/users/{id} https://www.example.com/users/123 id: "123"
www.example.com/users/{first}-{last} https://www.example.com/users/john-doe first: "john", last: "doe"
www.example.com/users/{id}/profile https://www.example.com/users//profile id: "" (cadena vacía)
www.example.com/users/user_{id} https://www.example.com/users/user_123 id: "123"
www.example.com/users/{userId}/posts/{postId} https://www.example.com/users/123/posts/456 userId: "123", postId: "456"
www.example.com/users/.* https://www.example.com/users/john-doe Ninguno
www.example.com/users https://www.example.com/users/ ❌ (La barra diagonal final crea un segmento adicional) N/A

Coincidencia de consultas

El orden de los parámetros de consulta en el URI de solicitud no necesita coincidir con el orden en el URI de patrón. Además, se ignoran los parámetros presentes en el URI de solicitud, pero no en el URI de patrón.

Se admiten los siguientes patrones de parámetros de consulta:

URI de patrón URI de solicitud Argumentos extraídos
www.example.com/users?name={name} https://www.example.com/users?name=john name: "john"
www.example.com/users?name={name} https://www.example.com/users?name= name: "" (cadena vacía)
www.example.com/users?{rawQuery} https://www.example.com/users?anything&else rawQuery: ["anything", "else"]
www.example.com/users?type=user_{id} https://www.example.com/users?type=user_123 id: "123"
www.example.com/users?name={first}_{last} https://www.example.com/users?name=john_doe first: "john", last: "doe"
www.example.com/users?list={list} https://www.example.com/users?list=10&list=20 list: ["10", "20"]
www.example.com/users?name={name}&{other} https://www.example.com/users?name=john&tab=info name: "john", other: ["tab=info"]
www.example.com/users?type=user_.* https://www.example.com/users?type=user_admin type: "admin"

Coincidencia de fragmentos

Se admiten los siguientes tipos de patrones de fragmentos:

URI de patrón URI de solicitud Argumentos extraídos
www.example.com/#section1 https://www.example.com/#section1 Ninguno
www.example.com/#section_{id} https://www.example.com/#section_123 id: "123"
www.example.com/#section_.* https://www.example.com/#section_123 Ninguno

Tipos de datos admitidos

UriDeepLinkMatcher admite la deserialización de argumentos de URI en tipos primitivos, enums, colecciones y objetos personalizados. La serialización se divide en dos categorías:

  • Serialización estándar: Usa kotlinx.serialization para deserializar en lo siguiente:
    • Primitivas (Boolean, Int, Long, Float, Double, Char, Byte, Short) y String
    • Enums
    • Set, List o Array de primitivas, cadenas o enums
    • Clases @Serializable anidadas (cuyas propiedades se aplanan en marcadores de posición de URI individuales)
  • Serialización personalizada con DeepLinkSerializer: Realiza conversiones entre un solo String y objetos personalizados, tipos externos (como java.time.LocalDate) o colecciones delimitadas personalizadas.

Serialización estándar

UriDeepLinkMatcher funciona de inmediato para tipos estándar y estructuras aplanadas sin necesidad de implementaciones de serializador personalizadas.

Primitivas y cadenas

UriDeepLinkMatcher decodifica automáticamente los tipos primitivos (Boolean, Int, Long, Float, Double, Char, Byte, Short) y String:

Enums

Los valores de enum se comparan con los nombres de los elementos de enum que distinguen mayúsculas de minúsculas:

Colecciones de consultas repetidas

Los parámetros de consulta con claves repetidas (como ?id=10&id=20) se deserializan automáticamente en List<T>, Set<T>, o Array<T>, donde T es un tipo primitivo, String, o enum:

Clases @Serializable anidadas

Cuando un NavKey contiene una propiedad cuyo tipo es otra clase @Serializable, UriDeepLinkMatcher aplana sus propiedades para que cada propiedad de la clase anidada se asigne directamente a un parámetro de URI individual con el mismo nombre:

Para deserializar objetos personalizados (como Filter(key = "brand", value = "pixel")), tipos externos (como java.time.LocalDate) o cadenas delimitadas personalizadas (como valores separados por comas), extiende DeepLinkSerializer<T>.

DeepLinkSerializer<T> es un KSerializer<T> abstracto que realiza conversiones entre un String y T:

abstract class DeepLinkSerializer<T : Any> : KSerializer<T> {
    abstract val serialName: String
    abstract fun deserialize(value: String): T
    abstract fun serialize(value: T): String
}

Por ejemplo, considera las definiciones Filter y FilterSerializer que se usan en los siguientes fragmentos:

Objetos personalizados únicos

Para decodificar un objeto de una sola cadena de parámetros de URI (como ?filter=brand:google), anota la propiedad con @Serializable(with = ...):

Objetos personalizados en parámetros de consulta repetidos

Para deserializar parámetros de consulta repetidos en una colección de objetos personalizados (List<T>, Set<T>, o Array<T>), implementa DeepLinkSerializer<T> para el tipo de elemento T y anota el argumento de tipo de la propiedad con @Serializable(with = ...):

Colecciones delimitadas en parámetros únicos

Para analizar valores separados por comas o delimitados personalizados (como ?ids=1,2,3) en una colección, implementa DeepLinkSerializer para el tipo de colección completo y anota la propiedad con @Serializable(with = ...):

Validación de argumentos y resultados coincidentes

UriDeepLinkMatcher distingue entre discrepancias (devuelve null para que se puedan intentar otros comparadores) y parámetros de configuración no compatibles (muestra una excepción).

Discrepancias

Se produce una discrepancia cuando un URI de solicitud entrante no cumple con los requisitos de patrón o tipo:

  • Faltan parámetros obligatorios: Propiedades clave que no admiten valores nulos sin valores predeterminados cuyos parámetros de URI correspondientes no están presentes en el URI de solicitud.
  • Errores de análisis de tipos: Valores de argumentos extraídos que no se pueden analizar en el tipo de propiedad esperado (por ejemplo, "abc" para una propiedad Int ).

Cuando se produce una discrepancia, UriDeepLinkMatcher.match devuelve null, lo que permite evaluar los comparadores posteriores.

Considera una clase clave y un comparador configurados con valores predeterminados, objetos anidados y enums:

En la siguiente tabla, se muestran los resultados coincidentes para varios URIs de solicitud:

URI de solicitud Resultado de la decodificación Resultado coincidente
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Listo (se proporcionaron todos los parámetros) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark Listo (zoom tiene el valor predeterminado 12, layer tiene el valor predeterminado STANDARD) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark Listo (el parámetro de consulta opcional vacío usa el valor predeterminado 12) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map?style=dark Discrepancia (falta el parámetro location obligatorio) null
https://www.example.com/map/paris?zoom=close&style=dark Discrepancia ("close" no es un Int) null
https://www.example.com/map/paris?style=dark&layer=HYBRID Discrepancia ("HYBRID" no está en enum) null

Parámetros de configuración no compatibles

Si tu clase clave contiene tipos de datos no compatibles, UriDeepLinkMatcher muestra una excepción durante la coincidencia en lugar de devolver null.

  • Maps y colecciones multidimensionales: UriDeepLinkMatcher solo admite colecciones unidimensionales de primitivas, cadenas, enums o tipos personalizados anotados con un DeepLinkSerializer. Los tipos Map arrojan un IllegalArgumentException, mientras que las colecciones anidadas (como List<List<String>>) arrojan un SerializationException.
  • Colecciones de objetos personalizados sin anotaciones: Las colecciones de tipos personalizados (como List<Filter>) arrojan un SerializationException, a menos que el tipo de elemento esté anotado con un DeepLinkSerializer.
  • Clases anidadas sin aplanar: Las clases anidadas @Serializable no se pueden asignar a un solo marcador de posición (como ?user={user}) sin un DeepLinkSerializer.

Comparación de UriMatchResult

Las instancias UriMatchResult se clasifican según los siguientes criterios en orden:

  1. Tipo MatchResult: UriMatchResult tiene una clasificación más alta que otros MatchResult tipos.
  2. Ruta de acceso exacta: Las coincidencias de ruta de acceso literales tienen una clasificación más alta que las coincidencias de comodines o marcadores de posición.
  3. Recuento de argumentos de ruta de acceso: Las coincidencias con más argumentos de ruta de acceso tienen una clasificación más alta.
  4. Presencia de argumentos: Las coincidencias que capturan argumentos tienen una clasificación más alta que las que no lo hacen.
  5. Recuento total de argumentos: La cantidad total de argumentos (ruta de acceso, consulta, fragmento) es el factor de desempate final.

Personaliza UriDeepLinkMatcher

UriDeepLinkMatcher es una clase open que puedes crear como subclase para personalizar la coincidencia de URI y el comportamiento de extracción de argumentos:

  • matchRequest: Es el punto de entrada de coincidencia de nivel superior para un DeepLinkRequest entrante. Anula esto para inspeccionar los extras de la solicitud o aplicar condiciones previas personalizadas antes de la coincidencia de URI.
  • matchUri: Compara el DeepLinkUri con el patrón configurado. Anula esto para interceptar y normalizar los URIs entrantes (por ejemplo, reescribir subdominios dinámicos o formatos de ruta de acceso heredados) antes de llamar a super.matchUri.
  • matchArguments: Deserializa los mapas de argumentos de ruta de acceso, consulta y fragmento extraídos en una instancia de clave de navegación con el serializer proporcionado. Anula esto para insertar valores dinámicos o transformar argumentos antes de la creación de instancias de clave.

En el siguiente ejemplo, se muestra la creación de subclases de UriDeepLinkMatcher para normalizar los prefijos de ruta de acceso de URL heredados antes de la coincidencia: