2016-08-29 8 views
22

मैं कुछ "डेटा" वर्ग ऑब्जेक्ट्स को समान "डेटा" क्लास ऑब्जेक्ट्स में कनवर्ट/मैप करना चाहता हूं। उदाहरण के लिए, डेटाबेस रिकॉर्ड्स के लिए कक्षाओं के लिए वेब फॉर्म के लिए कक्षाएं।डेटा ऑब्जेक्ट्स को कोटलिन डेटा ऑब्जेक्ट्स को मैप करने का बेहतर तरीका

data class PersonForm(
    val firstName: String, 
    val lastName: String, 
    val age: Int, 
    // maybe many fields exist here like address, card number, etc. 
    val tel: String 
) 
// maps to ... 
data class PersonRecord(
    val name: String, // "${firstName} ${lastName}" 
    val age: Int, // copy of age 
    // maybe many fields exist here like address, card number, etc. 
    val tel: String // copy of tel 
) 

मैं जावा में इस तरह के कार्यों के लिए ModelMapper उपयोग करते हैं, लेकिन यह क्योंकि डेटा वर्गों अंतिम होते हैं (ModelMapper CGLib प्रॉक्सी बनाता मानचित्रण परिभाषाओं को पढ़ने के लिए) नहीं किया जा सकता। जब हम इन वर्गों/फ़ील्ड को खोलते हैं तो हम मॉडलमैपर का उपयोग कर सकते हैं, लेकिन हमें मैन्युअल रूप से "डेटा" कक्षा की विशेषताओं को लागू करना होगा। (सीएफ मॉडलमैपर उदाहरण: https://github.com/jhalterman/modelmapper/blob/master/examples/src/main/java/org/modelmapper/gettingstarted/GettingStartedExample.java)

कोटलिन में ऐसे "डेटा" ऑब्जेक्ट्स को कैसे मैप करें?

अद्यतन: मॉडलमैपर स्वचालित रूप से ऐसे फ़ील्ड को मानचित्र करता है जिनके पास घोषणाओं को मैप किए बिना समान नाम (जैसे टेल -> टेल) है। मैं इसे कोटलिन के डेटा क्लास के साथ करना चाहता हूं।

अद्यतन: प्रत्येक कक्षा का उद्देश्य किस प्रकार के आवेदन पर निर्भर करता है, लेकिन इन्हें शायद किसी एप्लिकेशन की विभिन्न परत में रखा जाता है।

उदाहरण के लिए

:

  • डेटाबेस से डेटा (डाटाबेस इकाई) एचटीएमएल फार्म के लिए डेटा को (मॉडल/देखें मॉडल) डेटाबेस

इन कक्षाओं के लिए आंकड़ों के

  • REST API का परिणाम समान, लेकिन समान नहीं हैं।

    मैं सामान्य कार्य से बचना चाहते हैं इन कारणों के लिए कहता है:

    • यह तर्क के आदेश पर निर्भर करता है। एक वर्ग के लिए एक फ़ंक्शन जिसमें कई प्रकार हैं (जैसे स्ट्रिंग) आसानी से टूटा जाएगा।
    • कई घोषणाएं बेकार हैं हालांकि अधिकांश मैपिंग को नामकरण सम्मेलन के साथ हल किया जा सकता है।

    बेशक, एक पुस्तकालय जिसमें समान सुविधा है, लेकिन कोटलिन सुविधा की जानकारी भी स्वागत है (जैसे ईसीएमएस्क्रिप्ट में फैलाना)।

  • +0

    कृपया वर्णन करें कि आप मैप किए गए वर्गों का उपयोग कैसे करना चाहते हैं। दो अलग-अलग डेटा प्रारूपों का उद्देश्य क्या है? – voddan

    +0

    डेटा मॉडल को डुप्लिकेशंस के बारे में कभी नहीं सुना (विरासत कोड के मामलों को छोड़कर)। आम तौर पर जिस डेटा के साथ आप काम कर रहे हैं (देखें मॉडल) ** ** वह डेटा है जिसे आपने डेटाबेस में रखा है। – voddan

    +0

    @ वोददान एक उपयोग केस डोमेन मॉडल के केवल कुछ हिस्सों को अलग-अलग एपीआई उपभोक्ताओं को बेनकाब करना होगा। डोमेन मॉडल के प्रति _view_ के अलग-अलग डीटीओ होने के नाते यानी [जेसनव्यू] (http://fasterxml.github.io/jackson-annotations/javadoc/2.1.1/com/fasterxml/jackson/annotation/JsonView.html) का उपयोग करने से कहीं अधिक क्लीनर है। IMHO – miensol

    उत्तर

    9

    क्या आप देख रहे हैं?

    data class PersonRecord(val name: String, val age: Int, val tel: String){  
        object ModelMapper { 
         fun from(form: PersonForm) = 
          PersonRecord(form.firstName + form.lastName, form.age, form.tel)   
        } 
    } 
    

    और उसके बाद:

    val personRecord = PersonRecord.ModelMapper.from(personForm) 
    
    +1

    जो ऑपरेशन आप लिखते हैं वह मैं करना चाहता हूं। लेकिन मैं मैपिंग घोषणाओं को कम करना चाहता हूं क्योंकि कई फ़ील्ड जिनमें समान नाम हैं (जैसे टेल -> टेलि) मौजूद हैं। मैं केवल विशेष नियम जैसे प्रथम नाम + lastName => नाम लिखना चाहता हूं। – sunnyone

    +0

    डाटागार्ड नामित फैलाव सम्मेलन प्रस्ताव इसके लिए उपयोगी हो सकता है? https://youtrack.jetbrains.com/issue/KT-15471 –

    3

    तुम सच में उस के लिए एक अलग वर्ग चाहते हैं? आप मूल डेटा वर्ग के लिए गुण जोड़ सकते हैं:

    data class PersonForm(
        val firstName: String, 
        val lastName: String, 
        val age: Int, 
        val tel: String 
    ) { 
        val name = "${firstName} ${lastName}" 
    } 
    
    +0

    एक स्पीरेट क्लास को nesessarilly की आवश्यकता नहीं है। लेकिन मैं तर्क के आदेश के आधार पर बचना चाहता हूं। – sunnyone

    +1

    @sunnyone आप अपने डेटा ऑब्जेक्ट्स का निर्माण करते समय हमेशा [नामांकित तर्क] (https://kotlinlang.org/docs/reference/functions.html#named-arguments) का उपयोग कर सकते हैं ताकि आप तर्कों पर निर्भर न हों में परिभाषित – mfulton26

    10
    1. सरल (सबसे अच्छा?):

      fun PersonForm.toPersonRecord() = PersonRecord(
           name = "$firstName $lastName", 
           age = age, 
           tel = tel 
      ) 
      
    2. परावर्तन (नहीं शानदार प्रदर्शन):

      fun PersonForm.toPersonRecord() = with(PersonRecord::class.primaryConstructor!!) { 
          val propertiesByName = PersonForm::class.memberProperties.associateBy { it.name } 
          callBy(args = parameters.associate { parameter -> 
           parameter to when (parameter.name) { 
            "name" -> "$firstName $lastName" 
            else -> propertiesByName[parameter.name]?.get([email protected]) 
           } 
          }) 
      } 
      
    3. कैश्ड प्रतिबिंब (ठीक प्रदर्शन नहीं बल्कि जितनी जल्दी # 1):

      open class Transformer<in T : Any, out R : Any> 
      protected constructor(inClass: KClass<T>, outClass: KClass<R>) { 
          private val outConstructor = outClass.primaryConstructor!! 
          private val inPropertiesByName by lazy { 
           inClass.memberProperties.associateBy { it.name } 
          } 
      
          fun transform(data: T): R = with(outConstructor) { 
           callBy(parameters.associate { parameter -> 
            parameter to argFor(parameter, data) 
           }) 
          } 
      
          open fun argFor(parameter: KParameter, data: T): Any? { 
           return inPropertiesByName[parameter.name]?.get(data) 
          } 
      } 
      
      val personFormToPersonRecordTransformer = object 
      : Transformer<PersonForm, PersonRecord>(PersonForm::class, PersonRecord::class) { 
          override fun argFor(parameter: KParameter, data: PersonForm): Any? { 
           return when (parameter.name) { 
            "name" -> with(data) { "$firstName $lastName" } 
            else -> super.argFor(parameter, data) 
           } 
          } 
      } 
      
      fun PersonForm.toPersonRecord() = personFormToPersonRecordTransformer.transform(this) 
      
    4. Storing Properties in a Map

      data class PersonForm(val map: Map<String, Any?>) { 
          val firstName: String by map 
          val lastName: String by map 
          val age: Int   by map 
          // maybe many fields exist here like address, card number, etc. 
          val tel: String   by map 
      } 
      
      // maps to ... 
      data class PersonRecord(val map: Map<String, Any?>) { 
          val name: String by map // "${firstName} ${lastName}" 
          val age: Int  by map // copy of age 
          // maybe many fields exist here like address, card number, etc. 
          val tel: String  by map // copy of tel 
      } 
      
      fun PersonForm.toPersonRecord() = PersonRecord(HashMap(map).apply { 
          this["name"] = "${remove("firstName")} ${remove("lastName")}" 
      }) 
      
    संबंधित मुद्दे