Links diretos de URI correspondentes

Para comparar URIs hierárquicos com padrões e extrair argumentos, use UriDeepLinkMatcher. Ele depende de kotlinx.serialization para desserializar argumentos correspondentes nas suas classes de chave.

Para criar um UriDeepLinkMatcher, forneça um padrão DeepLinkUri e o serializador para a chave correspondente:

Para URIs não hierárquicos ou esquemas personalizados (como tel:), consulte Criar correspondências de link direto personalizadas.

Padrões de correspondência aceitos

UriDeepLinkMatcher faz a correspondência de URIs com base nos cinco componentes: esquema, autoridade, caminho, consulta e fragmento. As seções a seguir descrevem a sintaxe de padrão compatível, os marcadores de posição de argumento e as regras de correspondência para cada componente.

Correspondência de esquema

Se nenhum esquema estiver presente no padrão de URI, http e https serão correspondidos. Para corresponder a um esquema específico, inclua-o no padrão. Como exceção, um esquema http em um padrão corresponde aos URIs de solicitação http e https, enquanto https em um padrão corresponde apenas às solicitações https.

URI do padrão URI da solicitação Correspondência
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 ✅

Correspondência de autoridade

UriDeepLinkMatcher realiza uma correspondência exata que não diferencia maiúsculas de minúsculas na autoridade do URI (host e porta opcional). Marcadores de posição ou caracteres curinga não são aceitos na autoridade, e nenhum argumento é extraído:

URI do padrão URI da solicitação Correspondência
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 ❌

Correspondência de caminho

Os seguintes padrões de caminho são compatíveis:

URI do padrão URI da solicitação Correspondência Argumentos extraídos
www.example.com/users https://www.example.com/users ✅ Nenhum
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: "" (string vazia)
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 ✅ Nenhum
www.example.com/users https://www.example.com/users/ ❌ (A barra final cria um segmento extra) N/A

Correspondência de consultas

A ordem dos parâmetros de consulta no URI de solicitação não precisa corresponder à ordem no URI de padrão. Além disso, os parâmetros presentes no URI de solicitação, mas não no URI de padrão, são ignorados.

Os seguintes padrões de parâmetro de consulta são compatíveis:

URI do padrão URI da solicitação 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: "" (string vazia)
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"

Correspondência de fragmentos

Os seguintes tipos de padrão de fragmento são compatíveis:

URI do padrão URI da solicitação Argumentos extraídos
www.example.com/#section1 https://www.example.com/#section1 Nenhum
www.example.com/#section_{id} https://www.example.com/#section_123 id: "123"
www.example.com/#section_.* https://www.example.com/#section_123 Nenhum

Tipos de dados com suporte

O UriDeepLinkMatcher oferece suporte à desserialização de argumentos de URI em tipos primitivos, tipos enumerados, coleções e objetos personalizados. A serialização se divide em duas categorias:

  • Serialização padrão: usa kotlinx.serialization para desserializar em:
    • Primitivos (Boolean, Int, Long, Float, Double, Char, Byte, Short) e String
    • Enumerações
    • Set, List ou Array de primitivos, strings ou tipos enumerados
    • Classes @Serializable aninhadas (com propriedades niveladas em marcadores de posição de URI individuais)
  • Serialização personalizada com DeepLinkSerializer: converte entre um único String e objetos personalizados, tipos externos (como java.time.LocalDate) ou coleções personalizadas.

Serialização padrão

O UriDeepLinkMatcher funciona de forma integrada para tipos padrão e estruturas niveladas sem exigir implementações de serializador personalizadas.

Primitivos e strings

O UriDeepLinkMatcher decodifica automaticamente tipos primitivos (Boolean, Int, Long, Float, Double, Char, Byte, Short) e String:

Enumerações

Os valores de tipo enumerado são comparados com os nomes dos elementos de tipo enumerado, diferenciando maiúsculas de minúsculas:

Coleções de consultas repetidas

Os parâmetros de consulta com chaves repetidas (como ?id=10&id=20) são desserializados automaticamente em List<T>, Set<T> ou Array<T>, em que T é um tipo primitivo, String ou tipo enumerado:

Classes @Serializable aninhadas

Quando um NavKey contém uma propriedade cujo tipo é outra classe @Serializable, o UriDeepLinkMatcher nivela as propriedades para que cada propriedade da classe aninhada seja mapeada diretamente para um parâmetro de URI individual com o mesmo nome:

Para desserializar objetos personalizados (como Filter(key = "brand", value = "pixel")), tipos externos (como java.time.LocalDate) ou strings delimitadas personalizadas (como valores separados por vírgulas), estenda DeepLinkSerializer<T>.

DeepLinkSerializer<T> é um KSerializer<T> abstrato que faz a conversão entre um String e um 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 exemplo, considere as definições de Filter e FilterSerializer usadas nos seguintes snippets:

Objetos personalizados únicos

Para decodificar um objeto de uma única string de parâmetro de URI (como ?filter=brand:google), use a anotação @Serializable(with = ...) na propriedade:

Objetos personalizados em parâmetros de consulta repetidos

Para desserializar parâmetros de consulta repetidos em uma coleção de objetos personalizados (List<T>, Set<T> ou Array<T>), implemente DeepLinkSerializer<T> para o tipo de elemento T e anote o argumento de tipo da propriedade com @Serializable(with = ...):

Coleções delimitadas em parâmetros únicos

Para analisar valores separados por vírgulas ou delimitados de forma personalizada (como ?ids=1,2,3) em uma coleção, implemente DeepLinkSerializer para o tipo de coleção inteiro e anote a propriedade com @Serializable(with = ...):

Validação de argumentos e resultados correspondentes

O UriDeepLinkMatcher distingue entre incompatibilidades (retorna null para que outros correspondentes possam ser tentados) e configurações sem suporte (gera uma exceção).

Incompatibilidades

Uma incompatibilidade ocorre quando um URI de solicitação recebido não atende aos requisitos de padrão ou tipo:

  • Parâmetros obrigatórios ausentes: propriedades de chave não anuláveis sem valores padrão em que os parâmetros de URI correspondentes estão ausentes do URI de solicitação.
  • Falhas de análise de tipo: valores de argumento extraídos que não podem ser analisados no tipo de propriedade esperado (por exemplo, "abc" para uma propriedade Int).

Quando ocorre uma incompatibilidade, UriDeepLinkMatcher.match retorna null, permitindo que os correspondentes subsequentes sejam avaliados.

Considere uma classe de chave e um matcher configurados com valores padrão, objetos aninhados e tipos enumerados:

A tabela a seguir demonstra os resultados de correspondência para vários URIs de solicitação:

URI da solicitação Resultado da decodificação Resultado da partida
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Sucesso (todos os parâmetros fornecidos) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark Sucesso (zoom assume como padrão 12, layer assume como padrão STANDARD) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark Sucesso (o parâmetro de consulta opcional vazio usa o padrão 12) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map?style=dark Incompatibilidade (parâmetro location obrigatório ausente) null
https://www.example.com/map/paris?zoom=close&style=dark Incompatibilidade ("close" não é um Int) null
https://www.example.com/map/paris?style=dark&layer=HYBRID Incompatibilidade ("HYBRID" não está no tipo enumerado) null

Configurações sem suporte

Se a classe de chave tiver tipos de dados incompatíveis, o UriDeepLinkMatcher vai gerar uma exceção durante a correspondência em vez de retornar null.

  • Mapas e coleções multidimensionais: o UriDeepLinkMatcher só oferece suporte a coleções unidimensionais de primitivos, strings, tipos enumerados ou tipos personalizados anotados com um DeepLinkSerializer. Os tipos Map geram um IllegalArgumentException, enquanto as coleções aninhadas (como List<List<String>>) geram um SerializationException.
  • Coleções de objetos personalizados não anotados: coleções de tipos personalizados (como List<Filter>) geram uma SerializationException, a menos que o tipo de elemento seja anotado com um DeepLinkSerializer.
  • Classes aninhadas não niveladas: as classes @Serializable aninhadas não podem ser mapeadas para um único marcador de posição (como ?user={user}) sem um DeepLinkSerializer.

Comparação de UriMatchResult

As instâncias UriMatchResult são classificadas usando os seguintes critérios em ordem:

  1. Tipo MatchResult: UriMatchResult tem uma classificação mais alta do que outros tipos de MatchResult.
  2. Caminho exato: as correspondências de caminho literal têm uma classificação mais alta do que as correspondências de marcador de posição ou de caracteres curinga.
  3. Contagem de argumentos de caminho: as correspondências com mais argumentos de caminho têm uma classificação mais alta.
  4. Presença de argumentos: as correspondências que capturam argumentos têm uma classificação mais alta do que as que não capturam.
  5. Contagem total de argumentos: o número total de argumentos (caminho, consulta, fragmento) é o critério de desempate final.

Personalizar UriDeepLinkMatcher

UriDeepLinkMatcher é uma classe open que pode ser usada como subclasse para personalizar a correspondência de URI e o comportamento de extração de argumentos:

  • matchRequest: ponto de entrada de correspondência de nível superior para um DeepLinkRequest de entrada. Substitua isso para inspecionar extras de solicitação ou aplicar condições prévias personalizadas antes da correspondência de URI.
  • matchUri: faz a correspondência de DeepLinkUri com o padrão configurado. Substitua isso para interceptar e normalizar URIs recebidos (por exemplo, reescrever subdomínios dinâmicos ou formatos de caminho legados) antes de chamar super.matchUri.
  • matchArguments: desserializa os mapas de argumentos de caminho, consulta e fragmento extraídos em uma instância de chave de navegação usando o serializer fornecido. Substitua isso para injetar valores dinâmicos ou transformar argumentos antes da instanciação da chave.

O exemplo a seguir demonstra a subclasse UriDeepLinkMatcher para normalizar prefixos de caminho do URL legados antes da correspondência: