برای تطبیق URI های سلسله مراتبی با الگوها و استخراج آرگومانها، از UriDeepLinkMatcher استفاده کنید. این ابزار برای deserialize کردن آرگومانهای تطبیق یافته در کلاسهای کلیدی شما، به 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ها را بر اساس پنج مؤلفهی آنها تطبیق میدهد: طرحواره (scheme)، اعتبار (authority)، مسیر (path)، پرسوجو (query) و قطعه قطعه (fragment). بخشهای زیر، سینتکس الگوی پشتیبانیشده، متغیرهای آرگومان و قوانین تطبیق برای هر مؤلفه را شرح میدهند.
تطبیق طرح
اگر هیچ طرحی در الگوی URI وجود نداشته باشد، هم http و هم https مطابقت داده میشوند. برای مطابقت با یک طرح خاص، آن را در الگو قرار دهید. به عنوان یک استثنا، یک طرح http در یک الگو با هر دو URI درخواست 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 (میزبان و پورت اختیاری) انجام میدهد. placeholderها یا wildcardها در مرجع پشتیبانی نمیشوند و هیچ آرگومانی استخراج نمیشود:
| الگوی آدرس اینترنتی (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 درخواستی نیازی به مطابقت با ترتیب آنها در 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) | درخواست آدرس اینترنتی | آرگومانهای استخراجشده |
|---|---|---|
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 از deserialize کردن آرگومانهای URI به انواع اولیه، enumها، مجموعهها و اشیاء سفارشی پشتیبانی میکند. Serialization به دو دسته تقسیم میشود:
- سریالسازی استاندارد : از
kotlinx.serializationبرای deserialize کردن به صورت زیر استفاده میکند:- مقادیر اولیه (
Boolean،Int،Long،Float،Double،Char،Byte،Short) وString - انومها
-
Set،ListیاArrayاز مقادیر اولیه، رشتهها یا enumها - کلاسهای تو در تو
@Serializable(که ویژگیهای آنها در متغیرهای URI مجزا قرار میگیرند)
- مقادیر اولیه (
- سریالسازی سفارشی با
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
برای deserialize کردن اشیاء سفارشی (مانند 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"))
اشیاء سفارشی در پارامترهای پرس و جوی مکرر
برای deserialize کردن پارامترهای پرسوجوی تکراری به مجموعهای از اشیاء سفارشی ( 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 مربوطه در URI درخواست وجود ندارند.
- خطاهای تجزیه نوع : مقادیر آرگومان استخراج شده که نمیتوانند به نوع ویژگی مورد انتظار تجزیه شوند (برای مثال،
"abc"برای یک ویژگیInt).
وقتی عدم تطابق رخ میدهد، UriDeepLinkMatcher.match null را برمیگرداند و امکان ارزیابی تطبیقدهندههای بعدی را فراهم میکند.
یک کلاس کلید و تطبیقدهنده را در نظر بگیرید که با مقادیر پیشفرض، اشیاء تو در تو و 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>() )
جدول زیر نتایج منطبق برای 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 یک استثنا ایجاد میکند.
- نقشهها و مجموعههای چندبعدی :
UriDeepLinkMatcherفقط از مجموعههای تکبعدی از مقادیر اولیه، رشتهها، enumها یا انواع سفارشی که با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دارد. - مسیر دقیق : تطابقهای مسیر تحتاللفظی رتبه بالاتری نسبت به تطابقهای placeholder یا wildcard دارند.
- تعداد آرگومان مسیر : تطابقهایی که آرگومانهای مسیر بیشتری داشته باشند، رتبه بالاتری دارند.
- وجود آرگومانها : تطابقهایی که آرگومانها را در بر میگیرند، رتبه بالاتری نسبت به تطابقهایی که ندارند، کسب میکنند.
- تعداد کل آرگومانها : تعداد کل آرگومانها (مسیر، پرسوجو، قطعه کد) آخرین معیار برای تعیین گره است.
سفارشیسازی UriDeepLinkMatcher
UriDeepLinkMatcher یک کلاس open است که میتوانید برای سفارشیسازی تطبیق URI و رفتار استخراج آرگومان، از آن زیرمجموعه بگیرید:
-
matchRequest: نقطه ورود تطبیق سطح بالا برای یکDeepLinkRequestورودی. این را برای بررسی موارد اضافی درخواست یا اعمال پیششرطهای سفارشی قبل از تطبیق URI، لغو کنید. -
matchUri:DeepLinkUriرا با الگوی پیکربندیشده مطابقت میدهد. برای رهگیری و نرمالسازی URIهای ورودی (برای مثال، بازنویسی زیردامنههای پویا یا قالبهای مسیر قدیمی) قبل از فراخوانیsuper.matchUri، این مورد را لغو کنید. -
matchArguments: مسیر استخراجشده، کوئری و آرگومانهای قطعه کد را با استفاده ازserializerارائهشده، به یک نمونه کلید ناوبری تبدیل میکند. این مورد را برای تزریق مقادیر پویا یا تبدیل آرگومانها قبل از نمونهسازی کلید، لغو کنید.
مثال زیر، زیرکلاسسازی UriDeepLinkMatcher را برای نرمالسازی پیشوندهای مسیر URL قدیمی قبل از تطبیق نشان میدهد:
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) } }