ডিসিরিয়ালাইজেশন: এটি কী, ডেটা পুনরুদ্ধার প্রক্রিয়া

লেখক: IT Sectr প্রকাশিত: 2026-03-08 পড়ার সময়: 9 মিনিট

ডিসিরিয়ালাইজেশন হল JSON, XML বা Protobuf ডেটা স্ট্রিম থেকে একটি অবজেক্ট পুনরুদ্ধার করার প্রক্রিয়া, যা রিমোট API-এর সাথে কাজ করা যেকোনো মোবাইল অ্যাপ্লিকেশনের জন্য অপরিহার্য। Apple Developer (2026) অনুসারে, আগত ডেটার ভুল প্রক্রিয়াকরণ ডিভাইসে ক্র্যাশের সাধারণ কারণগুলির মধ্যে একটি। iOS-এ JSONDecoder এবং Android-এ Gson হল মানক টুল, কিন্তু প্রতিটির নিজস্ব বৈশিষ্ট্য এবং সীমাবদ্ধতা রয়েছে।

মূল বিষয়

  • ডিসিরিয়ালাইজেশন কোডে ব্যবহারের জন্য JSON, XML বা Protobuf থেকে টাইপকৃত অবজেক্ট পুনরুদ্ধার।
  • Codable কোড জেনারেশন সমর্থন সহ স্বয়ংক্রিয় ডিসিরিয়ালাইজেশনের জন্য Apple-এর প্রোটোকল।
  • Moshi বিভিন্ন পরিস্থিতির জন্য codegen এবং reflection অপশন সহ Square-এর Android লাইব্রেরি।
  • Type mismatch সবচেয়ে সাধারণ ত্রুটি যখন JSON ফিল্ডের ধরন এবং মডেলের বৈশিষ্ট্য মেলে না।
  • kotlinx.serialization কম্পাইলার-নিরাপদ কোড জেনারেশন সহ JetBrains-এর অফিসিয়াল সমাধান।

ডিসিরিয়ালাইজেশন কী?

ডিসিরিয়ালাইজেশন হল বাইট স্ট্রিম বা কাঠামোবদ্ধ টেক্সটকে প্রোগ্রামিং ভাষার অবজেক্টে রূপান্তরের প্রক্রিয়া। মোবাইল ডেভেলপমেন্টে, এই প্রক্রিয়াটি প্রতিবার ঘটে যখন একটি অ্যাপ্লিকেশন সার্ভার থেকে প্রতিক্রিয়া পায়: একটি JSON স্ট্রিং User, Order বা Product ক্লাসের ইনস্ট্যান্সে পরিণত হয়। ব্যবহারকারীকে ডেটা দেখানো স্ক্রিনের স্থায়িত্ব সরাসরি ডিসিরিয়ালাইজেশনের সঠিকতার উপর নির্ভর করে।

সিরিয়ালাইজেশন থেকে পার্থক্য

সিরিয়ালাইজেশন এবং ডিসিরিয়ালাইজেশন পারস্পরিক বিপরীত প্রক্রিয়া, বাস্তবে খুব কমই প্রতিসম। সিরিয়ালাইজেশন সার্ভারে পাঠানোর জন্য একটি অবজেক্টকে স্ট্রিংয়ে রূপান্তর করে, যখন ডিসিরিয়ালাইজেশন প্রাপ্ত স্ট্রিং থেকে অবজেক্ট পুনরুদ্ধার করে। সার্ভার এমন একটি ফিল্ড পাঠাতে পারে যা ক্লায়েন্ট মডেলে নেই, ভিন্ন তারিখ বিন্যাস ব্যবহার করতে পারে বা সংখ্যার পরিবর্তে null ফিরিয়ে দিতে পারে। Square Engineering (2025) অনুসারে, বিন্যাসের অসমতা Android অ্যাপ্লিকেশনে 23% নেটওয়ার্ক স্তরের ত্রুটির কারণ। ঝুঁকি কমাতে, OpenAPI-এর মাধ্যমে স্কিমা ভার্সনিং এবং কঠোর চুক্তি নির্দিষ্টকরণ ব্যবহার করা হয়।

ডিসিরিয়ালাইজেশনের জন্য ডেটা বিন্যাস

JSON মানুষের পঠনযোগ্যতা এবং অন্তর্নির্মিত সমর্থনের কারণে মোবাইল API-এর জন্য সর্বাধিক জনপ্রিয় বিন্যাস হিসাবে রয়ে গেছে। Google-এর Protobuf উচ্চ-লোড সিস্টেমে ব্যবহৃত হয় — এটি JSON-এর তুলনায় 3-6 গুণ বেশি কমপ্যাক্ট এবং দ্রুত পার্স হয়, কিন্তু .proto ফাইল থেকে কোড জেনারেশন প্রয়োজন এবং টুল ছাড়া পড়া যায় না। XML আধুনিক মোবাইল অ্যাপ্লিকেশনে কম সাধারণ, তবুও এটি এন্টারপ্রাইজ সিস্টেমের SOAP পরিষেবা এবং Android কনফিগারেশন ফাইলে ব্যবহৃত হয়। MessagePack একটি বাইনারি বিন্যাস যা গঠনে JSON-এর মতো কিন্তু আরও কমপ্যাক্ট, রিয়েল-টাইম সিস্টেমে জনপ্রিয়।

ডিসিরিয়ালাইজেশন কীভাবে কাজ করে

ডিসিরিয়ালাইজেশন প্রক্রিয়া তিনটি ধাপ অতিক্রম করে। প্রথমে, টোকেনাইজেশন কাঁচা টেক্সটকে টোকেনে বিভক্ত করে: কী, স্ট্রিং, সংখ্যা এবং বিভাজক। তারপর বাক্যগত বিশ্লেষণ গঠনের সঠিকতা পরীক্ষা করে — বন্ধনী বন্ধ আছে কিনা, উদ্ধৃতি চিহ্নের ধরন সঠিক কিনা, বিন্যাস RFC 8259 মেনে চলে কিনা। চূড়ান্ত ধাপ হল অ্যাপ্লিকেশনের অবজেক্ট মডেলে ম্যাপিং, যেখানে প্রতিটি JSON কী-কে নামকরণ কৌশল বিবেচনা করে একটি ক্লাস বৈশিষ্ট্য নির্ধারণ করা হয়।

Reflection বনাম Code generation

মোবাইল ডেভেলপমেন্টে ম্যাপিংয়ের দুটি পদ্ধতি উদ্ভূত হয়েছে। Reflection (Gson, JSONSerialization) Java Reflection API বা Objective-C রানটাইমের মাধ্যমে রানটাইমে ক্লাস গঠন বিশ্লেষণ করে — এটি নমনীয় এবং অতিরিক্ত কনফিগারেশনের প্রয়োজন হয় না, কিন্তু ধীর এবং বেশি মেমরি খরচ করে। Code generation (Moshi codegen, kotlinx.serialization, Codable) কম্পাইল সময়ে কোড তৈরি করে: দ্রুত, টাইপ-নিরাপদ এবং reflection-এর মাধ্যমে অভ্যন্তরীণ গঠন প্রকাশ করে না। JetBrains এবং Square প্রোডাকশন বিল্ডের জন্য কোড জেনারেশন সুপারিশ করে — Google বেঞ্চমার্কে পারফরম্যান্স লাভ 2-4 গুণ পর্যন্ত পৌঁছায়।

swift
struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date
}

let json = """
{
    "id": 42,
    "name": "Alice",
    "email": "alice@example.com",
    "created_at": "2026-06-01T12:00:00Z"
}
"""
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let user = try decoder.decode(User.self, from: data)

Swift-এ User মডেলে JSON ডিসিরিয়ালাইজেশনের উদাহরণ। convertFromSnakeCase কৌশল স্বয়ংক্রিয়ভাবে snake_case API কী-গুলিকে camelCase মডেল বৈশিষ্ট্যে রূপান্তর করে — iOS প্রকল্পে একটি মানক অভ্যাস। data প্যারামিটার হল URLSession-এর মাধ্যমে প্রাপ্ত সার্ভার প্রতিক্রিয়ার কাঁচা বাইট। try-এর মাধ্যমে ত্রুটি ব্যবস্থাপনা অ্যাপ্লিকেশন ক্র্যাশ না করে ত্রুটিপূর্ণ JSON ধরতে দেয়।

ডিকোডিং কৌশলের ভূমিকা

JSONDecoder চারটি কী কৌশল সমর্থন করে: useDefaultKeys (সঠিক মিল), convertFromSnakeCase (snake_case → camelCase), custom (closure) এবং convertFromKebabCase (kebab-case → camelCase)। তারিখের জন্য, .iso8601, .secondsSince1970, .millisecondsSince1970 এবং কাস্টম dateFormatter উপলব্ধ। সঠিক কৌশল নির্বাচন করা মজবুত ডিসিরিয়ালাইজেশনের দিকে প্রথম পদক্ষেপ, যা বেশিরভাগ বিন্যাস অমিল ত্রুটি প্রতিরোধ করে।

iOS-এ ডিসিরিয়ালাইজেশন

JSONDecoder iOS SDK-তে ডিসিরিয়ালাইজেশনের মানক প্রক্রিয়া, যা Codable প্রোটোকলের সাথে কাজ করে। JSONDecoder স্বয়ংক্রিয়ভাবে JSON-কে struct বা class ইনস্ট্যান্সে পার্স করে, নেস্টেড অবজেক্ট, অ্যারে এবং প্রিমিটিভ সমর্থন করে। কাস্টম লজিকের জন্য init(from: Decoder) পদ্ধতি ব্যবহার করা হয় — এটি অ-মানক বিন্যাস, পুরানো API সংস্করণে অনুপস্থিত ফিল্ড হ্যান্ডেল করতে বা একাধিক JSON কী-কে একটি বৈশিষ্ট্যে একত্রিত করতে দেয়।

swift
struct Order: Decodable {
    let orderId: String
    let amount: Double
    let status: OrderStatus

    enum OrderStatus: String, Decodable {
        case pending, confirmed, shipped, cancelled
    }
}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let order = try decoder.decode(Order.self, from: jsonData)

DateDecodingStrategy নির্ধারণ করে কিভাবে JSONDecoder তারিখ স্ট্রিং ব্যাখ্যা করে। .iso8601 সবচেয়ে বেশি ব্যবহৃত হয় — মানক REST API বিন্যাস। নেস্টেড enum OrderStatus স্বয়ংক্রিয়ভাবে JSON স্ট্রিং মান থেকে ডিকোড হয়। এটি ম্যাজিক নম্বর এড়ায় এবং কোডকে স্ব-ডকুমেন্টিং করে তোলে — অর্ডার স্থিতির সর্বদা কঠোরভাবে সংজ্ঞায়িত মানের সেট থাকে।

Codable-এ Property Wrappers

Swift 4.2 থেকে, Codable পৃথক বৈশিষ্ট্যের কাস্টম ডিসিরিয়ালাইজেশনের জন্য property wrappers সমর্থন করে। @DefaultValue একটি জনপ্রিয় wrapper যা JSON-এ ফিল্ড অনুপস্থিত থাকলে ডিফল্ট মান সেট করে। @LosslessString একটি স্ট্রিংকে সংখ্যায় এবং বিপরীতে রূপান্তর করে। এটি বিশেষভাবে উপযোগী যখন সার্ভার id-কে স্ট্রিং "123" হিসেবে পাঠায় কিন্তু মডেল Int আশা করে। Property wrappers init(from:)-এ বয়লারপ্লেট কোড কমায় এবং মডেলকে পরিষ্কার করে।

Android-এ ডিসিরিয়ালাইজেশন

Android-এ, ডিসিরিয়ালাইজেশন লাইব্রেরির পছন্দ ভাষা এবং প্রকল্পের প্রয়োজনীয়তার উপর নির্ভর করে। Google-এর Gson সবচেয়ে সাধারণ বিকল্প, যা reflection-এর মাধ্যমে কাজ করে কিন্তু জটিল শ্রেণিবিন্যাসে কর্মক্ষমতা সমস্যা রয়েছে। Square-এর Moshi reflection এবং code generation উভয়ই সমর্থন করে, কম মেমরি খরচ করে এবং বড় প্রতিক্রিয়া দ্রুত প্রক্রিয়া করে। JetBrains-এর kotlinx.serialization কম্পাইলার সংহতকরণ সহ একটি নেটিভ Kotlin সমাধান যা একেবারেই reflection ব্যবহার করে না।

kotlin
@Serializable
data class User(
    @SerialName("user_id")
    val userId: Int,
    val name: String,
    val email: String,
    @SerialName("created_at")
    val createdAt: String
)

val json = Json { ignoreUnknownKeys = true }
val user = json.decodeFromString<User>(response)

@Serializable একটি Kotlin কম্পাইলার অ্যানোটেশন যা ক্লাসের জন্য কোড জেনারেশন সক্রিয় করে। ignoreUnknownKeys প্যারামিটার ক্র্যাশ প্রতিরোধ করে যদি সার্ভার মডেলে অনুপস্থিত একটি ফিল্ড পাঠায়। snake_case কী ম্যাপিংয়ের জন্য @SerialName ব্যবহার করা হয় — iOS-এর convertFromSnakeCase-এর সমতুল্য। JetBrains (2026) অনুসারে, লাইব্রেরিটি মাল্টিপ্ল্যাটফর্ম সমর্থন করে: একই Serializable ক্লাস Android, iOS (KMP) এবং সার্ভার-সাইড Kotlin-এ কাজ করে।

Gson, Moshi এবং kotlinx.serialization-এর তুলনা

লাইব্রেরিগুলির মধ্যে পছন্দ গতি-নমনীয়তা আপসের উপর নির্ভর করে। Gson প্রোটোটাইপ এবং Java প্রকল্পের জন্য ভাল — এটির অ্যানোটেশনের প্রয়োজন নেই এবং এটি বাক্সের বাইরে কাজ করে। Moshi মধ্যম অবস্থান নেয়: @JsonClass(generateAdapter = true)-এর মাধ্যমে codegen kotlinx.serialization-এর কাছাকাছি গতি দেয়, যখন reflection মোড Gson-এর নমনীয়তা প্রদান করে। kotlinx.serialization খাঁটি Kotlin প্রকল্পের জন্য দ্রুততম বিকল্প কিন্তু Kotlin 1.4+ এবং Gradle-এ Kotlin Serialization প্লাগইন প্রয়োজন।

লাইব্রেরিপ্রক্রিয়াগতিKMP
GsonReflectionনিম্ননা
MoshiReflection / Codegenমধ্যম / উচ্চনা
kotlinx.serializationকম্পাইলার codegenউচ্চহ্যাঁ

সাধারণ ত্রুটি এবং তাদের প্রতিরোধ

Type mismatch এমন একটি অবস্থা যেখানে JSON-এ এক ধরনের মান থাকে কিন্তু মডেল অন্যটি আশা করে। সার্ভার সংখ্যার পরিবর্তে স্ট্রিং "42" পাঠিয়েছে, বা বুলিয়ান true-এর পরিবর্তে সংখ্যা 1 পাঠিয়েছে। iOS-এ, JSONDecoder ডিফল্টভাবে DecodingError.typeMismatch নিক্ষেপ করবে; Android-এ, Gson রূপান্তরের চেষ্টা করবে, যখন Moshi এবং kotlinx.serialization-এর স্পষ্ট অ্যাডাপ্টার প্রয়োজন। সমাধান হল নির্দিষ্ট ফিল্ডের জন্য lenient কৌশল বা কাস্টম ডিসিরিয়ালাইজার ব্যবহার করা।

অনুপস্থিত ফিল্ড এবং Nullable

যখন সার্ভার একটি ঐচ্ছিক ফিল্ড অন্তর্ভুক্ত করে না, কোড ত্রুটির সাথে ক্র্যাশ করে। Swift-এ Optional ফিল্ড এবং Kotlin-এ nullable টাইপ সমস্যার সমাধান করে: যদি ফিল্ড null হয় বা JSON-এ অনুপস্থিত থাকে, বৈশিষ্ট্যটি nil/null পায় এবং অ্যাপ্লিকেশন কাজ চালিয়ে যায়। বাধ্যতামূলক ফিল্ডের জন্য, ডিসিরিয়ালাইজেশনের আগে API ক্লায়েন্ট স্তরে তাদের উপস্থিতি পরীক্ষা করা ভাল। Moshi এবং kotlinx.serialization ডিফল্টভাবে সব ফিল্ড প্রয়োজন — nullable চিহ্নিতকরণ এবং ডিফল্ট মান এই সীমাবদ্ধতা সরিয়ে দেয়।

API সংস্করণ অসামঞ্জস্যতা

সার্ভারে JSON গঠনের পরিবর্তন প্রোডাকশন ক্র্যাশের একটি সাধারণ উৎস। মানক অভ্যাস হল রুট অবজেক্টে version ফিল্ডের মাধ্যমে স্কিমা ভার্সনিং এবং ক্লায়েন্টে 2-3 পূর্ববর্তী সংস্করণ সমর্থন করা। kotlinx.serialization বিভিন্ন সংস্করণের জন্য একাধিক মডেল ঘোষণা করতে এবং JsonElement-এ প্রাথমিক পার্সিংয়ের পরে version ফিল্ডের ভিত্তিতে সঠিক মডেল নির্বাচন করতে দেয়। অতিরিক্ত সুরক্ষার মধ্যে নতুন ফিল্ডের জন্য ignoreUnknownKeys এবং যে ফিল্ডগুলি সরানো হতে পারে তার জন্য ডিফল্ট মান অন্তর্ভুক্ত।

ত্রুটিলক্ষণসুরক্ষা সহ লাইব্রেরি
Type mismatchDecodingError / ব্যতিক্রমkotlinx — coerceInputValues = true
অনুপস্থিত ফিল্ডঅ্যাক্সেসে ক্র্যাশMoshi — @Transient + default
ভুল তারিখ বিন্যাসডিকোডিং ত্রুটিJSONDecoder — dateDecodingStrategy
অতিরিক্ত ফিল্ডউপেক্ষিত বা ক্র্যাশkotlinx — ignoreUnknownKeys = true
অ-নুল ফিল্ডে Nullরানটাইম ক্র্যাশMoshi — lenient @Nullable সহ

ডিসিরিয়ালাইজেশন ত্রুটি লগিং প্রোডাকশনে একটি বাধ্যতামূলক অভ্যাস। decode-কে do/catch-এ মোড়ানো, কাঁচা JSON এবং প্রত্যাশিত মডেল টাইপ Crashlytics বা Sentry-তে লগ করুন। এটি দ্রুত সনাক্ত করতে সাহায্য করবে কোন API-এর কোন ফিল্ড ভেঙেছে এবং কোন অ্যাপ্লিকেশন সংস্করণে। লগিং ছাড়া, ডিসিরিয়ালাইজেশন ত্রুটি প্রসঙ্গ ছাড়া একটি রহস্যময় ক্র্যাশের মতো দেখায়।

প্রায়শই জিজ্ঞাসিত প্রশ্ন

ডিসিরিয়ালাইজেশন পার্সিং থেকে কীভাবে আলাদা?

পার্সিং হল টাইপকৃত মডেল তৈরি না করেই কাঠামোবদ্ধ টেক্সটকে উপাদানে বিশ্লেষণ করা। ডিসিরিয়ালাইজেশন হল পার্সিংয়ের একটি নির্দিষ্ট ক্ষেত্রে যার ফলাফল পরিচিত বৈশিষ্ট্যের ধরন সহ একটি পূর্ণাঙ্গ ভাষার অবজেক্ট। পার্সিং স্ট্রিমিং হতে পারে, ডিসিরিয়ালাইজেশন সর্বদা একটি সম্পূর্ণ অবজেক্ট তৈরি করে।

নতুন Android প্রকল্পের জন্য কোন ডিসিরিয়ালাইজেশন লাইব্রেরি নির্বাচন করবেন?

খাঁটি Kotlin প্রকল্পের জন্য, kotlinx.serialization সুপারিশ করা হয় — এটি কম্পাইলারে সংহত, reflection ব্যবহার করে না এবং Kotlin Multiplatform সমর্থন করে। বিদ্যমান Java প্রকল্পের জন্য — Moshi code generation সহ। Gson লিগ্যাসি প্রকল্পের জন্য ছেড়ে দেওয়া ভাল যেখানে এটি প্রতিস্থাপন করতে উল্লেখযোগ্য প্রচেষ্টা লাগবে।

কী করবেন যদি সার্ভার snake_case পাঠায় কিন্তু মডেল camelCase ব্যবহার করে?

iOS-এ, JSONDecoder-এ keyDecodingStrategy = .convertFromSnakeCase ব্যবহার করুন। Android-এ kotlinx.serialization-এ, প্রতিটি ফিল্ডের জন্য @SerialName ব্যবহার করুন। Moshi-তে, @Json(name="field_name") বা গ্লোবাল JsonAdapter.Factory প্রয়োগ করুন। প্রকল্প স্তরে একটি সঙ্গতিশৈলী API চুক্তিতে সম্মত একটি সেরা অভ্যাস।

কেন ডিসিরিয়ালাইজেশন প্রোডাকশনে ক্র্যাশ করে কিন্তু ডেভেলপমেন্টে নয়?

সবচেয়ে সাধারণ কারণ বাধ্যতামূলক ঘোষিত ফিল্ডে সার্ভার থেকে অপ্রত্যাশিত null। ডেভেলপমেন্টে, সার্ভার সম্পূর্ণ ডেটা ফেরত দেয়; প্রোডাকশনে, এটি একটি সংক্ষিপ্ত প্রতিক্রিয়া ফেরত দেয়। সমাধান: সমস্ত সম্ভাব্য অনুপস্থিত ফিল্ডকে Kotlin-এ nullable বা Swift-এ optional হিসেবে চিহ্নিত করুন, ignoreUnknownKeys এবং ডিফল্ট মান ব্যবহার করুন।

ডিসিরিয়ালাইজেশনে কোনটি দ্রুত — Reflection নাকি Code generation?

Code generation (Moshi codegen, kotlinx.serialization, Codable) Google বেঞ্চমার্কে reflection-এর চেয়ে 2-4 গুণ দ্রুত। গতি ছাড়াও, কোড জেনারেশন টাইপ-নিরাপদ, রানটাইমে ক্লাস মেটাডেটার প্রয়োজন হয় না এবং টাইপ ত্রুটিগুলি ডিসিরিয়ালাইজেশনের সময় নয় বরং কম্পাইল সময়ে ধরা পড়ে।

সারসংক্ষেপ

  • ডিসিরিয়ালাইজেশন একটি মৌলিক মোবাইল ডেভেলপমেন্ট প্রক্রিয়া যা অ্যাপ্লিকেশন কোডে ব্যবহারের জন্য JSON, XML বা Protobuf থেকে অবজেক্ট পুনরুদ্ধার করে।
  • iOS JSONDecoder ব্যবহার করে Codable প্রোটোকলের সাথে, যা কী এবং তারিখ কৌশল সহ JSON থেকে মডেলে স্বয়ংক্রিয় রূপান্তর প্রদান করে।
  • Android তিনটি টool অফার করে: Gson (reflection), Moshi (reflection/codegen) এবং kotlinx.serialization (@Serializable-এর মাধ্যমে কম্পাইলার জেনারেশন)।
  • সাধারণ ত্রুটি — type mismatch, অনুপস্থিত ফিল্ড, অ-নাল ফিল্ডে null এবং API সংস্করণ অসামঞ্জস্যতা — nullable টাইপ, ignoreUnknownKeys এবং ভার্সনিং দ্বারা প্রতিরোধ করা হয়।
  • Code generation reflection-এর চেয়ে নিরাপদ এবং দ্রুত, তাই মোবাইল অ্যাপ্লিকেশনের প্রোডাকশন বিল্ডের জন্য সুপারিশ করা হয়।
  • ম্যাপিং কৌশল — iOS-এ keyDecodingStrategy এবং Android-এ @SerialName সার্ভার এবং ক্লায়েন্টের মধ্যে নামকরণ শৈলীর অমিল সমাধান করে।
  • প্রোডাকশন ঘটনার দ্রুত নির্ণয়ের জন্য Crashlytics বা Sentry-তে ডিসিরিয়ালাইজেশন ত্রুটি লগ করতে ভুলবেন না।

আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব

IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।

প্রকল্প নিয়ে আলোচনা করুন

আরও পড়ুন