Links diretos de URI correspondentes

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

Para criar um UriDeepLinkMatcher, forneça um padrão DeepLinkUri e o serializador da 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 com suporte

UriDeepLinkMatcher corresponde a URIs com base nos cinco componentes: esquema, autoridade, caminho, consulta e fragmento. As seções a seguir descrevem a sintaxe de padrão com suporte, 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 correspondentes. Para corresponder a um esquema específico, inclua-o no padrão. Como exceção, um http esquema em um padrão corresponde a URIs de solicitação http e https, enquanto https em um padrão corresponde apenas a solicitações https.

URI de padrão URI de 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 de 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 de padrão URI de 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

Há suporte para os seguintes padrões de caminho:

URI de padrão URI de 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 consulta

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.

Há suporte para os seguintes padrões de parâmetro de consulta:

URI de padrão URI de 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 fragmento

Há suporte para os seguintes tipos de padrão de fragmento:

URI de padrão URI de 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

UriDeepLinkMatcher oferece suporte à desserialização de argumentos de URI em tipos primitivos, enumerações, 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 enumerações
    • Classes @Serializable aninhadas (cujas propriedades são niveladas em marcadores de posição de URI individuais)
  • Serialização personalizada com DeepLinkSerializer: converte entre uma única String e objetos personalizados, tipos externos (como java.time.LocalDate) ou coleções delimitadas personalizadas.

Serialização padrão

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

Primitivos e strings

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

Enumerações

Os valores de enumeração são correspondidos com diferenciação de maiúsculas e minúsculas aos nomes dos elementos de enumeração:

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 enumeração:

Classes @Serializable aninhadas

Quando uma NavKey contém uma propriedade cujo tipo é outra classe @Serializable, UriDeepLinkMatcher nivela as propriedades dela para que cada propriedade da classe aninhada seja mapeada diretamente para um parâmetro de URI individual do 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írgula), estenda DeepLinkSerializer<T>.

DeepLinkSerializer<T> é um KSerializer<T> abstrato que converte entre uma String e 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 que são usadas nos snippets a seguir:

Objetos personalizados únicos

Para decodificar um objeto de uma única string de parâmetro de URI (como ?filter=brand:google), anote a propriedade com @Serializable(with = ...):

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írgula ou delimitados personalizados (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

UriDeepLinkMatcher distingue entre incompatibilidades (retorna null para que outras correspondências possam ser tentadas) 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 cujos 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 correspondente configurados com valores padrão, objetos aninhados e enumerações:

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

URI de solicitação Resultado da decodificação Resultado da correspondência
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 padrão é 12, layer é 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á na enumeração) null

Configurações sem suporte

Se a classe de chave contiver tipos de dados sem suporte, UriDeepLinkMatcher vai gerar uma exceção durante a correspondência em vez de retornar null.

  • Mapas e coleções multidimensionais: UriDeepLinkMatcher oferece suporte apenas a coleções unidimensionais de primitivos, strings, enumerações ou tipos personalizados anotados com um DeepLinkSerializer. Map tipos geram uma IllegalArgumentException, enquanto as coleções aninhadas (como List<List<String>>) geram uma 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: classes aninhadas @Serializable não podem ser mapeadas para um único marcador de posição (como ?user={user}) sem um DeepLinkSerializer.

Comparação de UriMatchResult

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

  1. Tipo MatchResult: UriMatchResult tem uma classificação mais alta do que outros MatchResult tipos.
  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 caractere 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 aquelas que não capturam.
  5. Contagem total de argumentos: o número total de argumentos (caminho, consulta, fragmento) é o desempate final.

Personalizar UriDeepLinkMatcher

UriDeepLinkMatcher é uma classe open que pode ser criada 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 recebido. Substitua isso para inspecionar extras de solicitação ou aplicar pré-condições personalizadas antes da correspondência de URI.
  • matchUri: corresponde ao DeepLinkUri ao 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 criação de subclasses de UriDeepLinkMatcher para normalizar prefixos de caminho do URL legados antes da correspondência: