برای مطابقت دادن نشانیهای وب سلسلهمراتبی با الگوها و استخراج آرگومانها، از
UriDeepLinkMatcher استفاده کنید. برای تبدیل کردن آرگومانهای منطبق به کلاسهای کلیدی شما، به kotlinx.serialization متکی است.
برای ایجاد UriDeepLinkMatcher، الگوی DeepLinkUri و
سریالساز را برای کلید مربوطه ارائه دهید:
@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")
برای نشانیهای وب غیر سلسلهمراتبی یا طرحهای سفارشی (مثل tel:)، به
ایجاد تطبیقدهندههای پیوند عمیق سفارشی مراجعه کنید.
الگوهای تطبیق پشتیبانیشده
UriDeepLinkMatcher نشانیهای وب را براساس پنج عنصر آنها مطابقت میدهد: طرح،
مرجع، مسیر، پُرسمان، و تکه. بخشهای زیر نحو الگوی پشتیبانیشده، جایبانهای آرگومان، و قوانین مطابقت برای هر عنصر را شرح میدهد.
تطبیق طرح
اگر هیچ طرحوارهای در الگوی نشانی وب وجود نداشته باشد، هم http و هم https مطابقت داده میشوند.
برای مطابقت با طرحی خاص، آن را در الگو بگنجانید. بهعنوان استثنا، طرحواره
http در الگو با هر دو نشانی وب درخواست http و https مطابقت دارد، درحالیکه
https در الگو فقط با درخواستهای https مطابقت دارد.
| الگوی نشانی وب | URI درخواست | مطابق |
|---|---|---|
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 |
✅ |
تطبیق مرجع
UriDeepLinkMatcher تطابق دقیق غیرحساس به حروف بزرگ و کوچک را در مرجع URI (میزبان و درگاه اختیاری) انجام میدهد. جایبانها یا نویسههای عام در مرجع پشتیبانی نمیشوند
و هیچ متغیری استخراج نمیشود:
| الگوی نشانی وب | URI درخواست | مطابق |
|---|---|---|
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 |
❌ |
تطبیق مسیر
الگوهای مسیر زیر پشتیبانی میشوند:
| الگوی نشانی وب | URI درخواست | مطابق | متغیرهای مستقل استخراجشده |
|---|---|---|---|
www.example.com/users |
https://www.example.com/users |
✅ | هیچکدام |
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: "" (رشته خالی) |
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 |
✅ | هیچکدام |
www.example.com/users |
https://www.example.com/users/ |
❌ (خط مورب انتهایی باعث ایجاد بخش اضافی میشود) | موجود نیست |
تطبیق پُرسمان
ترتیب پارامترهای پُرسمان در «شناسه منبع یکنواخت» درخواست لازم نیست با ترتیب در «شناسه منبع یکنواخت» الگو مطابقت داشته باشد. علاوهبراین، پارامترهای موجود در نشانی وب درخواست اما نه در نشانی وب الگو نادیده گرفته میشوند.
الگوهای پارامتر پُرسمان زیر پشتیبانی میشوند:
| الگوی نشانی وب | URI درخواست | متغیرهای مستقل استخراجشده |
|---|---|---|
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: "" (رشته خالی) |
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" |
تطبیق بخش
انواع الگوی تکهکد زیر پشتیبانی میشود:
| الگوی نشانی وب | URI درخواست | متغیرهای مستقل استخراجشده |
|---|---|---|
www.example.com/#section1 |
https://www.example.com/#section1 |
هیچکدام |
www.example.com/#section_{id} |
https://www.example.com/#section_123 |
id: "123" |
www.example.com/#section_.* |
https://www.example.com/#section_123 |
هیچکدام |
انواع دادههای پشتیبانیشده
UriDeepLinkMatcher از تبدیل کردن آرگومانهای نشانی وب به انواع اولیه،
شمارشها، مجموعهها، و اشیای سفارشی پشتیبانی میکند. سریالسازی در دو دسته قرار میگیرد:
- سریالسازی استاندارد: از
kotlinx.serializationبرای واسریالسازی کردن به:- استفاده میکند
- عناصر اولیه (
Boolean،Int،Long،Float،Double،Char،Byte،Short) وString - شمارشیها
-
Set،List، یاArrayاز انواع اولیه، رشتهها، یا شمارشیها -
@Serializableکلاس تودرتو (که داراییهای آنها در جایبانهای منفرد نشانی وب مسطح شده است)
- عناصر اولیه (
- سریالسازی سفارشی با
DeepLinkSerializer: بینStringتکی و اشیای سفارشی، انواع خارجی (مثلjava.time.LocalDate)، یا مجموعههای با جداکننده سفارشی تبدیل میکند.
سریالسازی استاندارد
UriDeepLinkMatcher برای انواع استاندارد و ساختارهای
مسطح بدون نیاز به پیادهسازیهای سریالساز سفارشی کار میکند.
عناصر اولیه و رشتهها
UriDeepLinkMatcher بهطور خودکار انواع اولیه (Boolean، Int،
Long، Float، Double، Char، Byte، Short) و 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 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)
مجموعههای پُرسمان تکراری
پارامترهای پُرسمان با کلیدهای تکراری (مثل ?id=10&id=20) بهطور خودکار به List<T>، Set<T>، یا Array<T> غیرسریالسازی میشوند که در آن T نوع ابتدایی، String، یا شمارشی است:
@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))
کلاسهای @Serializable تودرتو
وقتی NavKey حاوی داراییای باشد که نوع آن کلاس @Serializable دیگری باشد،
UriDeepLinkMatcher داراییهای آن را مسطح میکند تا هر دارایی کلاس تودرتو
مستقیماً به پارامتر نشانی وب مجزایی با همان نام نگاشت شود:
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))
سریالسازی سفارشی با DeepLinkSerializer
برای سریالزدایی کردن اشیای سفارشی (مثل Filter(key = "brand", value =
"pixel"))، انواع خارجی (مثل java.time.LocalDate)، یا رشتههای جداشده سفارشی (مثل مقادیر جداشده با کاما)، DeepLinkSerializer<T> را گسترش دهید.
DeepLinkSerializer<T> یک KSerializer<T> انتزاعی است که بین String و T تبدیل میکند:
abstract class DeepLinkSerializer<T : Any> : KSerializer<T> {
abstract val serialName: String
abstract fun deserialize(value: String): T
abstract fun serialize(value: T): String
}
برای مثال، تعریفهای Filter و FilterSerializer را که در گزیدههای زیر استفاده شده است درنظر بگیرید:
@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}" }
اشیاء سفارشی تکی
برای رمزگشایی کردن شیء از رشته پارامتر نشانی وب تکی (مثل
?filter=brand:google)، دارایی را با @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"))
اشیا سفارشی در پارامترهای پُرسمان تکراری
برای سریالزدایی پارامترهای پُرسمان تکراری در مجموعهای از اشیای سفارشی
(List<T>، Set<T>، یا Array<T>)، DeepLinkSerializer<T> را برای
نوع عنصر T پیادهسازی کنید و آرگومان نوع دارایی را با
@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")))
مجموعههای جداشده در پارامترهای تکی
برای تجزیه کردن مقادیر جداشده با ویرگول یا جداکنندههای سفارشی (مثل ?ids=1,2,3) به
مجموعه، DeepLinkSerializer را برای کل نوع مجموعه پیادهسازی کنید
و دارایی را با @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))
اعتبارسنجی آرگومان و نتایج مطابقت
UriDeepLinkMatcher بین عدم تطابقها (null را برمیگرداند تا
تطابقدهندههای دیگر امتحان شوند) و پیکربندیهای پشتیبانینشده (استثنایی
ایجاد میکند) تمایز قائل میشود.
عدم تطابقها
عدم تطابق زمانی رخ میدهد که نشانی وب درخواست ورودی با الگوی موردنظر یا الزامات نوع مطابقت نداشته باشد:
- پارامترهای الزامی وجود ندارد: داراییهای کلیدی غیرقابلتهی بدون مقادیر پیشفرض که پارامترهای نشانی وب متناظر آنها در نشانی وب درخواست وجود ندارد.
- خطاهای تجزیه نوع: مقادیر آرگومان استخراجشدهای که نمیتوانند به نوع دارایی موردانتظار تجزیه شوند (برای مثال،
"abc"برای داراییInt).
وقتی عدم تطابق رخ میدهد، UriDeepLinkMatcher.match مقدار null را برمیگرداند و به
تطبیقدهندههای بعدی اجازه میدهد ارزیابی شوند.
کلاس کلیدی و تطبیقدهندهای را که با مقادیر پیشفرض، اشیای تودرتو، و شمارشگرها پیکربندی شده است درنظر بگیرید:
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>() )
جدول زیر نتایج منطبق را برای شناسههای URI درخواستهای مختلف نشان میدهد:
| URI درخواست | نتیجه رمزگشایی | نتیجه مسابقه |
|---|---|---|
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE |
موفقیتآمیز (همه پارامترها ارائه شده است) | UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE))) |
https://www.example.com/map/paris?style=dark |
موفقیتآمیز (zoom بهطور پیشفرض روی 12، layer روی STANDARD تنظیم میشود) |
UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD))) |
https://www.example.com/map/paris?zoom=&style=dark |
موفقیتآمیز (پارامتر پُرسمان اختیاری خالی از پیشفرض 12 استفاده میکند) |
UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD))) |
https://www.example.com/map?style=dark |
عدم تطابق (پارامتر الزامی location وجود ندارد) |
null |
https://www.example.com/map/paris?zoom=close&style=dark |
تطابق نداشتن ("close" Int نیست) |
null |
https://www.example.com/map/paris?style=dark&layer=HYBRID |
تطابق نداشتن ("HYBRID" در شمارش نیست) |
null |
پیکربندیهای پشتیبانینشده
اگر کلاس کلید شما حاوی انواع داده پشتیبانینشده باشد، UriDeepLinkMatcher بهجای برگرداندن null،
درطول مطابقت استثنایی ایجاد میکند.
- Maps و مجموعههای چندبعدی:
UriDeepLinkMatcherفقط از مجموعههای تکبعدی از عناصر اولیه، رشتهها، شمارشها، یا انواع سفارشی که باDeepLinkSerializerحاشیهنویسی شدهاند پشتیبانی میکند. MapنوعIllegalArgumentExceptionرا ایجاد میکند، درحالیکه مجموعههای تودرتو (مثلList<List<String>>)SerializationExceptionرا ایجاد میکنند. - مجموعههای شیء سفارشی بدون شرح: مجموعههای انواع سفارشی (مثل
List<Filter>) خطایSerializationExceptionایجاد میکنند، مگر اینکه نوع عنصر باDeepLinkSerializerشرحگذاری شده باشد. - کلاسهای تودرتوی غیرمسطح: کلاسهای تودرتوی
@Serializableنمیتوانند بدونDeepLinkSerializerبه یک جایبان (مثل?user={user}) نگاشت شوند.
// 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
مقایسه UriMatchResult
موارد UriMatchResult بااستفاده از معیارهای زیر به ترتیب رتبهبندی میشوند:
- نوع MatchResult:
UriMatchResultنسبت به دیگر انواعMatchResultرتبه بالاتری دارد. - مسیر دقیق: مطابقتهای مسیر واقعی نسبتبه مطابقتهای جایبان یا نویسه عام رتبه بالاتری دارند.
- تعداد متغیرهای مستقل مسیر: مطابقت با متغیرهای مستقل مسیر بیشتر رتبه بالاتری دارد.
- وجود آرگومانها: مطابقتهایی که آرگومانها را ضبط میکنند نسبتبه مواردی که این کار را نمیکنند رتبه بالاتری دارند.
- تعداد کل آرگومانها: تعداد کل آرگومانها (مسیر، پُرسمان، تکه) بهعنوان عامل نهایی تعیین برنده درنظر گرفته میشود.
سفارشیسازی UriDeepLinkMatcher
UriDeepLinkMatcher یک کلاس open است که میتوانید آن را زیرکلاس کنید تا رفتار استخراج آرگومان و مطابقت نشانی وب را سفارشیسازی کنید:
matchRequest: نقطه ورود تطبیق سطح بالا برایDeepLinkRequestورودی. برای بازرسی کردن موارد اضافی درخواست یا اعمال پیششرطهای سفارشی قبلاز مطابقت با نشانی وب، این مورد را ملغی کنید.matchUri:DeepLinkUriرا با الگوی پیکربندیشده مطابقت میدهد. برای رهگیری و عادیسازی کردن نشانیهای وب ورودی (برای مثال، بازنویسی زیردامنههای پویا یا قالبهای مسیر قدیمی) قبلاز فراخوانیsuper.matchUri، این را ملغی کنید.matchArguments: نقشههای آرگومان مسیر، پُرسمان، و تکهبرداری استخراجشده را بااستفاده ازserializerارائهشده به نمونه کلید ناوبری واسریالسازی میکند. برای تزریق مقادیر پویا یا تبدیل آرگومانها قبلاز نمونهسازی کلید، این را ملغی کنید.
مثال زیر نشان میدهد چطور میتوان UriDeepLinkMatcher را برای عادیسازی پیشوندهای مسیر نشانی وب قدیمی قبلاز مطابقت دادن زیرطبقهبندی کرد:
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) } }