Untuk mencocokkan URI hierarkis dengan pola dan mengekstrak argumen, gunakan
UriDeepLinkMatcher. Fitur ini mengandalkan kotlinx.serialization untuk mendeserialisasi argumen yang cocok ke dalam class kunci Anda.
Untuk membuat UriDeepLinkMatcher, berikan pola DeepLinkUri dan
serializer untuk kunci yang sesuai:
@Serializable data class UserProfileKey(val id: String) : NavKey val userProfilePattern = DeepLinkUri("www.example.com/users/{id}") val userProfileMatcher = UriDeepLinkMatcher(userProfilePattern, serializer<UserProfileKey>()) val request = DeepLinkRequest(uri = "https://www.example.com/users/123") val matchResult = userProfileMatcher.match(request) val key = matchResult?.key // UserProfileKey(id = "123")
Untuk URI non-hierarkis atau skema kustom (seperti tel:), lihat
Membuat pencocok deep link kustom.
Pola pencocokan yang didukung
UriDeepLinkMatcher mencocokkan URI berdasarkan lima komponennya: skema, otoritas, jalur, kueri, dan fragmen. Bagian berikut menjelaskan sintaksis pola, placeholder argumen, dan aturan pencocokan yang didukung untuk setiap komponen.
Pencocokan skema
Jika tidak ada skema dalam pola URI, http dan https akan cocok.
Untuk mencocokkan skema tertentu, sertakan dalam pola. Sebagai pengecualian, skema
http dalam pola cocok dengan URI permintaan http dan https, sedangkan
https dalam pola hanya cocok dengan permintaan https.
| URI Pola | URI Permintaan | Cocok |
|---|---|---|
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 |
✅ |
Pencocokan otoritas
UriDeepLinkMatcher melakukan pencocokan persis yang tidak peka huruf besar/kecil pada otoritas URI (host dan port opsional). Placeholder atau karakter pengganti tidak didukung dalam otoritas, dan tidak ada argumen yang diekstrak:
| URI Pola | URI Permintaan | Cocok |
|---|---|---|
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 |
❌ |
Pencocokan jalur
Pola jalur berikut didukung:
| URI Pola | URI Permintaan | Cocok | Argumen yang Diekstrak |
|---|---|---|---|
www.example.com/users |
https://www.example.com/users |
✅ | Tidak ada |
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 kosong) |
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 |
✅ | Tidak ada |
www.example.com/users |
https://www.example.com/users/ |
❌ (Garis miring di akhir membuat segmen tambahan) | T/A |
Pencocokan kueri
Urutan parameter kueri di URI permintaan tidak harus cocok dengan urutan di URI pola. Selain itu, parameter yang ada di URI permintaan, tetapi tidak ada di URI pola akan diabaikan.
Pola parameter kueri berikut didukung:
| URI Pola | URI Permintaan | Argumen yang Diekstrak |
|---|---|---|
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 kosong) |
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" |
Pencocokan fragmen
Jenis pola fragmen berikut didukung:
| URI Pola | URI Permintaan | Argumen yang Diekstrak |
|---|---|---|
www.example.com/#section1 |
https://www.example.com/#section1 |
Tidak ada |
www.example.com/#section_{id} |
https://www.example.com/#section_123 |
id: "123" |
www.example.com/#section_.* |
https://www.example.com/#section_123 |
Tidak ada |
Jenis data yang didukung
UriDeepLinkMatcher mendukung deserialisasi argumen URI ke dalam jenis primitif, enum, koleksi, dan objek kustom. Serialisasi dibagi menjadi dua kategori:
- Serialisasi standar: Menggunakan
kotlinx.serializationuntuk mendeserialisasi ke:- Primitif (
Boolean,Int,Long,Float,Double,Char,Byte,Short) danString - Enum
Set,List, atauArraydari primitif, string, atau enum- Class
@Serializablebertingkat (yang propertinya diratakan ke dalam placeholder URI individual)
- Primitif (
- Serialisasi kustom dengan
DeepLinkSerializer: Mengonversi antara satuStringdan objek kustom, jenis eksternal (sepertijava.time.LocalDate), atau koleksi yang dibatasi kustom.
Serialisasi standar
UriDeepLinkMatcher dapat langsung digunakan untuk jenis standar dan struktur yang diratakan tanpa memerlukan implementasi serializer kustom.
Primitif dan string
UriDeepLinkMatcher otomatis mendekode jenis primitif (Boolean, Int, Long, Float, Double, Char, Byte, Short) dan String:
@Serializable data class UserProfileKey(val id: Int) : NavKey val matcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/users/{id}"), serializer<UserProfileKey>() ) val request = DeepLinkRequest(uri = "https://www.example.com/users/123") val key = matcher.match(request)?.key // UserProfileKey(id = 123)
Enum
Nilai enum dicocokkan dengan peka huruf besar/kecil terhadap nama elemen enum:
enum class SortOrder { RELEVANCE, DATE, POPULARITY } @Serializable data class ProductsKey(val sort: SortOrder) : NavKey val matcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/products?sort={sort}"), serializer<ProductsKey>() ) val request = DeepLinkRequest(uri = "https://www.example.com/products?sort=DATE") val key = matcher.match(request)?.key // ProductsKey(sort = SortOrder.DATE)
Koleksi kueri berulang
Parameter kueri dengan kunci berulang (seperti ?id=10&id=20) otomatis
dideserialisasi ke dalam List<T>, Set<T>, atau Array<T> dengan T adalah jenis primitif
type, String, atau enum:
@Serializable data class FilteredItemsKey(val ids: List<Int>) : NavKey val matcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/items?id={ids}"), serializer<FilteredItemsKey>() ) val request = DeepLinkRequest(uri = "https://www.example.com/items?id=10&id=20") val key = matcher.match(request)?.key // FilteredItemsKey(ids = listOf(10, 20))
Class @Serializable bertingkat
Jika NavKey berisi properti yang jenisnya adalah class @Serializable lain, UriDeepLinkMatcher akan meratakan propertinya sehingga setiap properti class bertingkat dipetakan langsung ke parameter URI individual dengan nama yang sama:
enum class SortOrder { RELEVANCE, DATE, POPULARITY } @Serializable data class SearchFilters( val category: String, val sortBy: SortOrder = SortOrder.RELEVANCE ) @Serializable data class SearchKey( val query: String, val page: Int = 1, // Flattened into {category} and {sortBy} val filters: SearchFilters ) : NavKey val matcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/search?q={query}&page={page}&category={category}&sortBy={sortBy}"), serializer<SearchKey>() ) val request = DeepLinkRequest(uri = "https://www.example.com/search?q=kotlin&category=books&sortBy=DATE") val key = matcher.match(request)?.key // SearchKey(query = "kotlin", page = 1, filters = SearchFilters(category = "books", sortBy = SortOrder.DATE))
Serialisasi kustom dengan DeepLinkSerializer
Untuk mendeserialisasi objek kustom (seperti Filter(key = "brand", value =
"pixel")), jenis eksternal (seperti java.time.LocalDate), atau string yang dibatasi kustom (seperti nilai yang dipisahkan koma), perluas DeepLinkSerializer<T>.
DeepLinkSerializer<T> adalah KSerializer<T> abstrak yang mengonversi
antara String dan T:
abstract class DeepLinkSerializer<T : Any> : KSerializer<T> {
abstract val serialName: String
abstract fun deserialize(value: String): T
abstract fun serialize(value: T): String
}
Misalnya, pertimbangkan definisi Filter dan FilterSerializer yang
digunakan dalam cuplikan berikut:
@Serializable data class Filter(val key: String, val value: String) object FilterSerializer : DeepLinkSerializer<Filter>() { override val serialName: String = "com.example.Filter" override fun deserialize(value: String): Filter { val parts = value.split(":", limit = 2) if (parts.size < 2) { throw SerializationException("Invalid filter: $value. Expected key:value.") } return Filter(key = parts[0], value = parts[1]) } override fun serialize(value: Filter): String = "${value.key}:${value.value}" }
Objek kustom tunggal
Untuk mendekode objek dari string parameter URI tunggal (seperti ?filter=brand:google), anotasi properti dengan @Serializable(with = ...):
@Serializable data class CatalogKey( @Serializable(with = FilterSerializer::class) val filter: Filter ) : NavKey val matcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/catalog?filter={filter}"), serializer<CatalogKey>() ) val request = DeepLinkRequest(uri = "https://www.example.com/catalog?filter=brand:google") val key = matcher.match(request)?.key // CatalogKey(filter = Filter("brand", "google"))
Objek kustom dalam parameter kueri berulang
Untuk mendeserialisasi parameter kueri berulang ke dalam koleksi objek kustom
(List<T>, Set<T>, atau Array<T>), terapkan DeepLinkSerializer<T> untuk
jenis elemen T dan anotasi argumen jenis properti dengan
@Serializable(with = ...):
@Serializable data class SearchResultsKey( val query: String, val filters: List<@Serializable(with = FilterSerializer::class) Filter> = emptyList() ) : NavKey val searchResultsPattern = DeepLinkUri("www.example.com/search?q={query}&filter={filters}") val searchResultsMatcher = UriDeepLinkMatcher(searchResultsPattern, serializer<SearchResultsKey>()) val request = DeepLinkRequest(uri = "https://www.example.com/search?q=phone&filter=brand:google&filter=color:hazel") val matchResult = searchResultsMatcher.match(request) val key = matchResult?.key // SearchResultsKey(query = "phone", filters = listOf(Filter("brand", "google"), Filter("color", "hazel")))
Koleksi yang dibatasi dalam parameter tunggal
Untuk mengurai nilai yang dipisahkan koma atau dibatasi kustom (seperti ?ids=1,2,3) ke dalam koleksi, terapkan DeepLinkSerializer untuk seluruh jenis koleksi dan anotasi properti dengan @Serializable(with = ...):
object IntListCsvSerializer : DeepLinkSerializer<List<Int>>() { override val serialName: String = "com.example.IntListCsv" override fun deserialize(value: String): List<Int> { if (value.isEmpty()) return emptyList() return value.split(",").map { it.trim().toInt() } } override fun serialize(value: List<Int>): String = value.joinToString(",") } @Serializable data class ItemListKey( @Serializable(with = IntListCsvSerializer::class) val ids: List<Int> ) : NavKey val itemListPattern = DeepLinkUri("www.example.com/items/{ids}") val itemListMatcher = UriDeepLinkMatcher(itemListPattern, serializer<ItemListKey>()) val request = DeepLinkRequest(uri = "https://www.example.com/items/10,20,30") val key = itemListMatcher.match(request)?.key // ItemListKey(ids = listOf(10, 20, 30))
Hasil pencocokan dan validasi argumen
UriDeepLinkMatcher membedakan antara ketidakcocokan (menampilkan null sehingga pencocok lain dapat dicoba) dan konfigurasi yang tidak didukung (menampilkan pengecualian).
Ketidakcocokan
Ketidakcocokan terjadi saat URI permintaan yang masuk tidak memenuhi persyaratan pola atau jenis:
- Parameter wajib tidak ada: Properti kunci yang tidak dapat di-null-kan tanpa nilai default yang parameter URI-nya tidak ada dari URI permintaan.
- Kegagalan penguraian jenis: Nilai argumen yang diekstrak yang tidak dapat diuraikan
ke dalam jenis properti yang diharapkan (misalnya,
"abc"untuk propertiInt).
Jika terjadi ketidakcocokan, UriDeepLinkMatcher.match akan menampilkan null, sehingga pencocok berikutnya dapat dievaluasi.
Pertimbangkan class kunci dan pencocok yang dikonfigurasi dengan nilai default, objek bertingkat, dan enum:
enum class MapLayer { STANDARD, SATELLITE, TERRAIN } @Serializable data class LayerOptions( val style: String, val layer: MapLayer = MapLayer.STANDARD ) @Serializable data class MapKey( val location: String, val zoom: Int = 12, val options: LayerOptions ) : NavKey val matcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/map/{location}?zoom={zoom}&style={style}&layer={layer}"), serializer<MapKey>() )
Tabel berikut menunjukkan hasil pencocokan untuk berbagai URI permintaan:
| URI Permintaan | Hasil Dekode | Hasil Pencocokan |
|---|---|---|
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE |
Berhasil (Semua parameter disediakan) | UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE))) |
https://www.example.com/map/paris?style=dark |
Berhasil (zoom default ke 12, layer ke STANDARD) |
UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD))) |
https://www.example.com/map/paris?zoom=&style=dark |
Berhasil (Parameter kueri opsional kosong menggunakan default 12) |
UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD))) |
https://www.example.com/map?style=dark |
Tidak cocok (Parameter location wajib tidak ada) |
null |
https://www.example.com/map/paris?zoom=close&style=dark |
Tidak cocok ("close" bukan Int) |
null |
https://www.example.com/map/paris?style=dark&layer=HYBRID |
Tidak cocok ("HYBRID" tidak ada dalam enum) |
null |
Konfigurasi yang tidak didukung
Jika class kunci Anda berisi jenis data yang tidak didukung, UriDeepLinkMatcher akan menampilkan pengecualian selama pencocokan, bukan menampilkan null.
- Peta dan koleksi multidimensi:
UriDeepLinkMatcherhanya mendukung koleksi primitif, string, enum, atau jenis kustom satu dimensi yang dianotasi denganDeepLinkSerializer.Mapjenis menampilkan anIllegalArgumentException, sedangkan koleksi bertingkat (sepertiList<List<String>>) menampilkanSerializationException. - Koleksi objek kustom yang tidak dianotasi: Koleksi jenis kustom (seperti
List<Filter>) menampilkanSerializationExceptionkecuali jika jenis elemen dianotasi denganDeepLinkSerializer. - Class bertingkat yang tidak diratakan: Class
@Serializabletidak dapat di petakan ke satu placeholder (seperti?user={user}) tanpaDeepLinkSerializer.
// Throws IllegalArgumentException: Map decoding is not supported. @Serializable data class InvalidKey(val tags: Map<String, String>) : NavKey // Throws SerializationException: Only collections of primitives are supported. @Serializable data class InvalidKey(val filters: List<Filter>) : NavKey
Perbandingan UriMatchResult
Instance UriMatchResult diberi peringkat menggunakan kriteria berikut secara berurutan:
- Jenis MatchResult:
UriMatchResultdiberi peringkat lebih tinggi daripada jenisMatchResultlainnya. - Jalur persis: Pencocokan jalur literal diberi peringkat lebih tinggi daripada pencocokan placeholder atau karakter pengganti.
- Jumlah argumen jalur: Pencocokan dengan lebih banyak argumen jalur diberi peringkat lebih tinggi.
- Keberadaan argumen: Pencocokan yang mengambil argumen diberi peringkat lebih tinggi daripada pencocokan yang tidak mengambil argumen.
- Jumlah argumen total: Jumlah total argumen (jalur, kueri, fragmen) adalah pemutus seri terakhir.
Menyesuaikan UriDeepLinkMatcher
UriDeepLinkMatcher adalah class open yang dapat Anda buat subclass-nya untuk menyesuaikan pencocokan URI dan perilaku ekstraksi argumen:
matchRequest: Titik entri pencocokan tingkat atas untukDeepLinkRequestyang masuk. Ganti ini untuk memeriksa tambahan permintaan atau menerapkan prasyarat kustom sebelum pencocokan URI.matchUri: MencocokkanDeepLinkUridengan pola yang dikonfigurasi. Ganti ini untuk mencegat dan menormalkan URI yang masuk (misalnya, menulis ulang subdomain dinamis atau format jalur lama) sebelum memanggilsuper.matchUri.matchArguments: Mendeserialisasi peta argumen jalur, kueri, dan fragmen yang diekstrak ke dalam instance kunci navigasi menggunakanserializeryang disediakan. Ganti ini untuk menyuntikkan nilai dinamis atau mengubah argumen sebelum pembuatan instance kunci.
Contoh berikut menunjukkan pembuatan subclass UriDeepLinkMatcher untuk menormalkan awalan jalur URL lama sebelum pencocokan:
class LegacyPrefixUriDeepLinkMatcher<T : Any>( uriPattern: DeepLinkUri, serializer: KSerializer<T> ) : UriDeepLinkMatcher<T>(uriPattern, serializer) { override fun matchUri(uri: DeepLinkUri): UriMatchResult<T>? { val path = uri.path val normalizedUri = if (path != null && path.startsWith("/legacy/")) { DeepLinkUri(uri.toString().replaceFirst("/legacy", "")) } else { uri } return super.matchUri(normalizedUri) } }