برای مطابقت دادن URIهای سلسلهمراتبی با الگوها و استخراج آرگومانها، از
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")
برای URIهای غیرسلسلهمراتبی یا طرحهای سفارشی (مانند tel:)، به
ایجاد تطبیقدهندههای پیوند عمیق سفارشی مراجعه کنید.
الگوهای تطبیق پشتیبانیشده
UriDeepLinkMatcher نشانیهای وب را براساس پنج مؤلفه آنها مطابقت میدهد: طرح،
مرجع، مسیر، پُرسمان، و قطعه. بخشهای زیر نحو الگوی پشتیبانیشده، جایبانهای شناسه، و قوانین مطابقت را برای هر مؤلفه شرح میدهند.
مطابقت طرح
اگر طرحوارهای در الگوی URI وجود نداشته باشد، هم http و هم https مطابقت داده میشوند.
برای مطابقت با طرحی خاص، آن را در الگو بگنجانید. بهعنوان استثنا، طرحواره
http در الگو با هر دو شناسه منبع یکسان درخواست http و https مطابقت دارد، درحالیکه
https در الگو فقط با درخواستهای https مطابقت دارد.
| URI الگو | 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 الگو | 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 الگو | 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 درخواست لازم نیست با ترتیب در URI الگو مطابقت داشته باشد. علاوهبراین، پارامترهای موجود در URI درخواست که در URI الگو وجود ندارند نادیده گرفته میشوند.
الگوهای پارامتر پُرسمان زیر پشتیبانی میشوند:
| URI الگو | 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 الگو | 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 از غیرسریالی کردن آرگومانهای URI به انواع اولیه،
enum، مجموعهها، و اشیای سفارشی پشتیبانی میکند. سریالسازی به دو دسته تقسیم میشود:
- سریالسازی استاندارد: از
kotlinx.serializationبرای سریالزدایی به این موارد استفاده میکند:- Primitives (
Boolean،Int،Long،Float،Double،Char،Byte،Short) وString - شمارشیها
-
Set،List، یاArrayاز انواع دادههای اولیه، رشتهها، یا شمارشیها - کلاسهای
@Serializableتودرتو (که ویژگیهای آنها در جایبانهای URI جداگانه مسطح شده است)
- Primitives (
- سریالسازی سفارشی با
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 بهصورت حروفحساس با نامهای عنصر 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)
مجموعههای پُرسمان تکراری
پارامترهای پُرسمان با کلیدهای تکراری (مانند ?id=10&id=20) بهطور خودکار
به List<T>، Set<T>، یا Array<T> که T نوعی اولیه،
String، یا 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))
کلاسهای @Serializable تودرتو
وقتی NavKey حاوی ویژگیای باشد که نوع آن کلاس @Serializable دیگری است،
UriDeepLinkMatcher ویژگیهای آن را مسطح میکند تا هر ویژگی کلاس تودرتو
مستقیماً به پارامتر URI جداگانهای با همان نام نگاشت شود:
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}" }
اشیای سفارشی تکی
برای رمزگشایی شیء از رشته پارامتر URI تکی (مانند
?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 را برمیگرداند تا
سایر تطبیقدهندهها بتوانند امتحان شوند) و پیکربندیهای پشتیبانینشده (استثنا
ایجاد میکند) تمایز قائل میشود.
ناسازگاریها
عدم تطابق زمانی رخ میدهد که شناسه منبع یکنواخت درخواست ورودی، الزامات الگو یا نوع را برآورده نکند:
- پارامترهای الزامی وجود ندارد: ویژگیهای کلیدی غیرقابلتهی بدون مقادیر پیشفرض که پارامترهای URI مربوطه آنها در URI درخواست وجود ندارد.
- خطاهای تجزیه نوع: مقادیر متغیر مستقل استخراجشده که نمیتوانند
به نوع ویژگی موردانتظار تجزیه شوند (برای مثال،
"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 درخواست | نتیجه رمزگشایی | نتیجه مسابقه |
|---|---|---|
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" در enum نیست) |
null |
پیکربندیهای پشتیبانینشده
اگر کلاس کلید شما حاوی انواع دادههای پشتیبانینشده باشد، UriDeepLinkMatcher
درطول تطبیق بهجای برگرداندن null، استثنایی را ایجاد میکند.
- مجموعههای چندبعدی و Maps:
UriDeepLinkMatcherفقط از مجموعههای تکبعدی انواع اصلی، رشتهها، شمارشیها، یا انواع سفارشی حاشیهنویسیشده باDeepLinkSerializerپشتیبانی میکند. انواعMapIllegalArgumentExceptionرا برمیگردانند، درحالیکه مجموعههای تودرتو (مانند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 است که میتوانید آن را زیرکلاس کنید تا رفتار استخراج متغیر مستقل و مطابقت URI را سفارشیسازی کنید:
matchRequest: نقطه ورودی تطبیق سطح بالا برایDeepLinkRequestورودی. این را لغو کنید تا موارد اضافی درخواست را بازرسی کنید یا پیششرطهای سفارشی را قبلاز تطبیق URI اعمال کنید.-
matchUri:DeepLinkUriرا با الگوی پیکربندیشده مطابقت میدهد. این را لغو کنید تا URIهای ورودی را قبلاز فراخوانیsuper.matchUriرهگیری و عادیسازی کنید (برای مثال، بازنویسی زیردامنههای پویا یا قالبهای مسیر قدیمی). matchArguments: Deserializes the extracted path, query, and fragment argument maps into a navigation key instance using the providedserializer. این را لغو کنید تا مقادیر پویا را تزریق کنید یا آرگومانها را قبلاز نمونهسازی کلید تبدیل کنید.
مثال زیر نشان میدهد که چطور میتوان زیرکلاس 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) } }