Vínculos directos de URI coincidentes

Para hacer coincidir los URI jerárquicos con patrones y extraer argumentos, usa UriDeepLinkMatcher. Se basa en kotlinx.serialization para deserializar los argumentos que coinciden en tus clases de claves.

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

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

Patrones de coincidencia admitidos

UriDeepLinkMatcher hace coincidir los URIs según 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 ningún esquema presente en el patrón de URI, se buscan coincidencias con http y https. Para que coincida con un esquema específico, inclúyelo en el patrón. Como excepción, un esquema http en un patrón coincide con los URI de solicitud http y https, mientras que https en un patrón solo coincide con las solicitudes https.

URI del 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 del 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 de acceso:

URI del 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

Concordancia de búsquedas

No es necesario que el orden de los parámetros de consulta en el URI de solicitud coincida 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 del 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 del 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, enumeraciones, 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 enumeraciones
    • Clases @Serializable anidadas (cuyas propiedades se aplanan en marcadores de posición de URI individuales)
  • Serialización personalizada con DeepLinkSerializer: Convierte 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 simplificadas sin necesidad de implementaciones de serializadores personalizados.

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 distinguiendo 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 compacta 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 convierte entre un String y un 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 de Filter y FilterSerializer que se usan en los siguientes fragmentos:

Objetos personalizados individuales

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

Objetos personalizados en parámetros de consulta repetidos

Para deserializar los 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 de forma personalizada (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 de coincidencias

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

Incongruencias

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

  • Faltan parámetros obligatorios: Son propiedades de clave que no admiten valores nulos sin valores predeterminados cuyos parámetros de URI correspondientes no están presentes en el URI de solicitud.
  • Fallas de análisis de tipos: Son 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 que se evalúen los comparadores posteriores.

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

En la siguiente tabla, se muestran los resultados de coincidencia para varios URI de solicitud:

URI de solicitud Resultado de la decodificación Resultado del partido
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Correcto (se proporcionaron todos los parámetros) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark Correcto (zoom se establece de forma predeterminada en 12, layer en STANDARD) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark Correcto (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 Incongruencia (falta el parámetro obligatorio location) null
https://www.example.com/map/paris?zoom=close&style=dark No coinciden ("close" no es un Int) null
https://www.example.com/map/paris?style=dark&layer=HYBRID No coincide ("HYBRID" no está en la enumeración) null

Parámetros de configuración no compatibles

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

  • Mapas y colecciones multidimensionales: UriDeepLinkMatcher solo admite colecciones unidimensionales de primitivas, cadenas, enumeraciones 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 una SerializationException, a menos que el tipo de elemento esté anotado con un DeepLinkSerializer.
  • Clases anidadas sin compactar: Las clases @Serializable anidadas no se pueden asignar a un solo marcador de posición (como ?user={user}) sin un DeepLinkSerializer.

Comparación de UriMatchResult

Las instancias de UriMatchResult se clasifican con los siguientes criterios en orden:

  1. Tipo MatchResult: UriMatchResult tiene una clasificación más alta que otros tipos de MatchResult.
  2. Ruta de acceso exacta: Las coincidencias de ruta de acceso literal tienen una clasificación más alta que las coincidencias de marcador de posición o comodí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 se clasifican más alto que las que no lo hacen.
  5. Recuento total de argumentos: La cantidad total de argumentos (ruta de acceso, consulta y fragmento) es el desempate final.

Personalizar UriDeepLinkMatcher

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

  • matchRequest: Es el punto de entrada coincidente 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: Coincide con 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 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 claves.

En el siguiente ejemplo, se muestra cómo crear una subclase de UriDeepLinkMatcher para normalizar los prefijos de ruta de URL heredados antes de la coincidencia: