2009-06-19 11 views
13

मैं XmlSerializer और विरासत का उपयोग करके कुछ ऑब्जेक्ट्स को क्रमबद्ध करने की कोशिश कर रहा हूं लेकिन मुझे परिणाम देने के साथ कुछ समस्याएं आ रही हैं।.NET Serialization ऑर्डरिंग

नीचे क्या मैं सेटअप के लिए इसी तरह एक उदाहरण है: ~

public class SerializableBase 
{ 
    [XmlElement(Order = 1)] 
    public bool Property1 { get; set;} 

    [XmlElement(Order = 3)] 
    public bool Property3 { get; set;} 
} 

[XmlRoot("Object")] 
public class SerializableObject1 : SerializableBase 
{ 
} 

[XmlRoot("Object")] 
public class SerializableObject2 : SerializableBase 
{ 
    [XmlElement(Order = 2)] 
    public bool Property2 { get; set;} 
} 

परिणाम मैं चाहता हूँ इस प्रकार है: ~

<Object> 
    <Property1></Property1> 
    <Property2></Property2> 
    <Property3></Property3> 
</Object> 

हालांकि मैं का एक परिणाम हो रही है: ~

<Object> 
    <Property1></Property1> 
    <Property3></Property3> 
    <Property2></Property2> 
</Object> 

क्या किसी को पता है कि यह संभव है या कोई विकल्प है?

धन्यवाद

+0

मुझे इस तरह की समस्या थी जहां मुझे एसओएपी संदेश में अंतिम बार दिखाई देने के लिए व्युत्पन्न कक्षा में संपत्ति की आवश्यकता थी, मेरा समाधान बेस क्लास में संपत्ति को आंतरिक रूप से जोड़ना था, फिर इसे "नया" कीवर्ड से छुपाएं व्युत्पन्न कक्षा में। मेरा जवाब देखें [यहां] (http://stackoverflow.com/questions/22174311/wcf-serialization-order-issue/22177272#22177272)। आशा करता हूँ की ये काम करेगा। –

उत्तर

3

ऐसा लगता है कि XmlSerializer वर्ग की तरह आधार प्रकार serializes और फिर व्युत्पन्न प्रकार इसी क्रम में और केवल व्यक्तिगत रूप से प्रत्येक वर्ग के भीतर आदेश संपत्ति का सम्मान किया जाता है। भले ही आदेश पूरी तरह से आप जो चाहते हैं वह नहीं है, फिर भी इसे ठीक से Deserialize करना चाहिए। यदि आपके पास वास्तव में ऑर्डर होना चाहिए तो आपको कस्टम एक्सएमएल सीरिएलाइज़र लिखना होगा। मैं उस बीक्यूज के खिलाफ सावधानी बरतूंगा। .NET XmlSerializer आपके लिए बहुत से हैंडलिंग करता है। क्या आप वर्णन कर सकते हैं कि आपको जरूरी क्रम में चीजों की आवश्यकता क्यों है?

3

संपादित करें: यह दृष्टिकोण काम नहीं करता है। मैंने पोस्ट छोड़ दिया है ताकि लोग इस सोच की रेखा से बच सकें।

धारावाहिक पुनरावर्तक कार्य करता है। इसका लाभ है; deserialization पर, deserialization प्रक्रिया बेस वर्ग, फिर व्युत्पन्न कक्षा पढ़ सकते हैं। इसका मतलब है कि व्युत्पन्न वर्ग पर एक संपत्ति आधार पर गुणों से पहले सेट नहीं है, जिससे समस्याएं पैदा हो सकती हैं।

यदि यह वास्तव में मायने रखती है (और मुझे यकीन है कि क्यों यह क्रम में इन पाने के लिए महत्वपूर्ण है नहीं कर रहा हूँ) तो आप इस कोशिश कर सकते हैं -

1) आधार वर्ग 'Property1 और Property3 आभासी बनाते हैं। 2) उन्हें अपने व्युत्पन्न वर्ग में छोटी संपत्तियों के साथ ओवरराइड करें। उदाहरण के लिए

public class SerializableBase 
{ 
    [XmlElement(Order = 1)] 
    public virtual bool Property1 { get; set;} 

    [XmlElement(Order = 3)] 
    public virtual bool Property3 { get; set;} 
} 

[XmlRoot("Object")] 
public class SerializableObject1 : SerializableBase 
{ 
} 

[XmlRoot("Object")] 
public class SerializableObject2 : SerializableBase 
{ 
    [XmlElement(Order = 1)] 
    public override bool Property1 
    { 
     get { return base.Property1; } 
     set { base.Property1 = value; } 
    } 

    [XmlElement(Order = 2)] 
    public bool Property2 { get; set;} 

    [XmlElement(Order = 3)] 
    public override bool Property3 
    { 
     get { return base.Property3; } 
     set { base.Property3 = value; } 
    } 

} 

यह सबसे व्युत्पन्न वर्ग पर संपत्ति का एक ठोस implementtion डालता है, और आदेश का सम्मान किया जाना चाहिए।

+0

मैंने यह कोशिश की - यह वास्तव में काम नहीं करता –

+0

आह, क्षमा करें - मैंने सोचा होगा कि उसने उस वर्ग को देखकर काम किया था जिसमें ठोस कार्यान्वयन था, लेकिन स्पष्ट रूप से नहीं। मैं यह इंगित करने के लिए पोस्ट अपडेट करूंगा कि यह दृष्टिकोण काम नहीं करता है। –

+0

यह अभी भी एक अच्छा विचार था :) –

15

तकनीकी रूप से, एक शुद्ध एक्सएमएल परिप्रेक्ष्य से, मैं कहूंगा कि यह संभवतः एक बुरी चीज है जो करना चाहता है।

.NET XmlSerialization जैसी चीजों की जटिलता को छुपाता है - इस मामले में, यह उस स्कीमा को छुपाता है जिस पर आपके धारावाहिक xml को अनुरूप होना चाहिए।

अनुमानित स्कीमा बेस प्रकार और एक्सटेंशन प्रकारों का वर्णन करने के लिए अनुक्रम तत्वों का उपयोग करेगा। इसके लिए सख्त आदेश की आवश्यकता होती है - भले ही Deserializer कम सख्त हो और ऑर्डर तत्वों से बाहर हो।

एक्सएमएल स्कीमा में, एक्सटेंशन प्रकारों को परिभाषित करते समय, कक्षा वर्ग के अतिरिक्त तत्व बेस क्लास के तत्वों के बाद आते हैं।

आप अनिवार्य रूप से एक स्कीमा कि (स्पष्टता के लिए हटाया एक्सएमएल-y टैग)

base 
    sequence 
    prop1 
    prop3 

derived1 extends base 
    sequence 
    <empty> 

derived2 extends base 
    sequence 
    prop2 

prop1 और prop3 के बीच में एक प्लेसहोल्डर रहना इंगित करने के लिए कोई तरीका नहीं है की तरह कुछ लग रहा है के लिए होता है जहां व्युत्पन्न से गुण एक्सएमएल जा सकते हैं।

अंत में, आपके डेटा प्रारूप और आपके व्यावसायिक ऑब्जेक्ट के बीच एक मेल नहीं है। शायद आपका सबसे अच्छा विकल्प किसी ऑब्जेक्ट को अपने एक्सएमएल क्रमबद्धरण से निपटने के लिए परिभाषित करना है।

उदाहरण

[XmlRoot("Object") 
public class SerializableObjectForPersistance 
{ 
    [XmlElement(Order = 1)] 
    public bool Property1 { get; set; } 

    [XmlElement(Order = 2, IsNullable=true)] 
    public bool Property2 { get; set; } 

    [XmlElement(Order = 3)] 
    public bool Property3 { get; set; } 
} 

के लिए यह अपने ऑब्जेक्ट मॉडल से अपने xml क्रमांकन कोड अलग करती है। SerializableObject1 या SerializableObject2 से SerializableObjectForPersistance पर सभी मानों की प्रतिलिपि बनाएँ, और फिर इसे क्रमबद्ध करें।

अनिवार्य रूप से, यदि आप अपने धारावाहिक एक्सएमएल के प्रारूप पर इस तरह के विशिष्ट नियंत्रण चाहते हैं जो एक्सएमएल सीरियलाइजेशन फ्रेमवर्क की उम्मीदों के साथ काफी उत्साहित नहीं है, तो आपको अपने व्यापार ऑब्जेक्ट डिज़ाइन (इस मामले में विरासत संरचना) और ज़िम्मेदारी को कम करने की आवश्यकता है उस व्यापार वस्तु के क्रमबद्ध करने के लिए।

+0

+1, स्कीमा एक महत्वपूर्ण लेकिन आसानी से अनदेखा बिंदु है। – shambulator

+1

आप एक बहुत अच्छा मुद्दा बनाते हैं कि क्यों आदेश "अपेक्षित" व्यवहार नहीं करता है, क्योंकि हम ओओपी के संदर्भ में सोच रहे हैं और एक्सएमएल स्कीमा नहीं। मेरे लंबवत मामले में, ढीला-युग्मन उपयुक्त डिजाइन नहीं है (फिर, यह आला है - हमेशा ढीले-युग्मन के लिए प्रयास करें!), और यदि यह आपकी स्थिति है, तो आप हमेशा एकत्रीकरण का प्रयास कर सकते हैं, जहां "बच्चा" ऑब्जेक्ट _contains_ "पैरेंट" ऑब्जेक्ट। आप अभी भी encapsulation और reusability प्राप्त करते हैं, लेकिन आप बच्चे के लिए, तत्वों के सटीक क्रम को भी निर्दिष्ट कर सकते हैं। – fourpastmidnight

+0

@Nader मैंने इस प्रश्न के उत्तर में आपके उत्तर का संदर्भ दिया। जबकि मेरा समाधान काम करता है, आपका स्पष्ट रूप से बेहतर है। जब मैं ढीला युग्मन कहता हूं तो मैं गलत था, मेरे डिजाइन में उचित नहीं था। अगर मुझे कभी रिएक्टर करने का मौका मिलता है, तो मैं आपकी सिफारिशों का उपयोग कर रहा हूं! – fourpastmidnight

0

नाडर की तरह, शायद अधिक ढीला-युग्मित डिज़ाइन बनाने के बारे में सोचें। हालांकि, मेरे मामले में, ढीला-युग्मन उचित नहीं था। यहां मेरी कक्षा पदानुक्रम है, और कस्टम सीरियलाइजेशन या डीटीओ का उपयोग किये बिना मैं समस्या को हल करने का प्रस्ताव कैसे करता हूं।

मेरी प्रोजेक्ट में, मैं एक XML दस्तावेज़ के टुकड़ों का प्रतिनिधित्व करने के लिए ऑब्जेक्ट्स का एक पूरा समूह बना रहा हूं जो एक वेब सेवा के माध्यम से सबमिट किया जाएगा। बहुत बड़ी संख्या में टुकड़े हैं। सभी को हर अनुरोध के साथ नहीं भेजा जाता है (असल में, इस उदाहरण में, मैं एक प्रतिक्रिया मॉडलिंग कर रहा हूं, लेकिन अवधारणाएं समान हैं)। इन टुकड़ों का उपयोग अनुरोध को इकट्ठा करने के लिए ब्लॉक बनाने की तरह किया जाता है (या इस मामले में प्रतिक्रिया को अलग करना)। तो विरासत पदानुक्रम के बावजूद वांछित क्रम को पूरा करने के लिए एकत्रीकरण/encapsulation का उपयोग करने का एक उदाहरण यहां दिया गया है।

[Serializable] 
public abstract class ElementBase 
{ 
    // This constructor sets up the default namespace for all of my objects. Every 
    // Xml Element class will inherit from this class. 
    internal ElementBase() 
    { 
     this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] { 
      new XmlQualifiedName(string.Empty, "urn:my-default-namespace:XSD:1") 
     }); 
    } 

    [XmlNamespacesDeclaration] 
    public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } } 
    private XmlSerializationNamespaces _namespaces; 
} 


[Serializable] 
public abstract class ServiceBase : ElementBase 
{ 
    private ServiceBase() { } 

    public ServiceBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null) 
    { 
     this._requestId = requestId; 
     this._asyncRequestId = asyncRequestId; 
     this._name = name; 
    } 

    public Guid RequestId 
    { 
     get { return this._requestId; } 
     set { this._requestId = value; } 
    } 
    private Guid _requestId; 

    public Guid? AsyncRequestId 
    { 
     get { return this._asyncRequestId; } 
     set { this._asyncRequestId = value; } 
    } 
    private Guid? _asyncRequestId; 

    public bool AsyncRequestIdSpecified 
    { 
     get { return this._asyncRequestId == null && this._asyncRequestId.HasValue; } 
     set { /* XmlSerializer requires both a getter and a setter.*/ ; } 
    } 

    public Identifier Name 
    { 
     get { return this._name; } 
     set { this._name; } 
    } 
    private Identifier _name; 
} 


[Serializable] 
public abstract class ServiceResponseBase : ServiceBase 
{ 
    private ServiceBase _serviceBase; 

    private ServiceResponseBase() { } 

    public ServiceResponseBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null, Status status = null) 
    { 
     this._serviceBase = new ServiceBase(requestId, asyncRequestId, name); 
     this._status = status; 
    } 

    public Guid RequestId 
    { 
     get { return this._serviceBase.RequestId; } 
     set { this._serviceBase.RequestId = value; } 
    } 

    public Guid? AsyncRequestId 
    { 
     get { return this._serviceBase.AsyncRequestId; } 
     set { this._serviceBase.AsyncRequestId = value; } 
    } 

    public bool AsynceRequestIdSpecified 
    { 
     get { return this._serviceBase.AsyncRequestIdSpecified; } 
     set { ; } 
    } 

    public Identifier Name 
    { 
     get { return this._serviceBase.Name; } 
     set { this._serviceBase.Name = value; } 
    } 

    public Status Status 
    { 
     get { return this._status; } 
     set { this._status = value; } 
    } 
} 

[Serializable] 
[XmlRoot(Namespace = "urn:my-default-namespace:XSD:1")] 
public class BankServiceResponse : ServiceResponseBase 
{ 
    // Determines if the class is being deserialized. 
    private bool _isDeserializing; 

    private ServiceResponseBase _serviceResponseBase; 

    // Constructor used by XmlSerializer. 
    // This is special because I require a non-null List<T> of items later on. 
    private BankServiceResponse() 
    { 
     this._isDeserializing = true; 
     this._serviceResponseBase = new ServiceResponseBase(); 
    } 

    // Constructor used for unit testing 
    internal BankServiceResponse(bool isDeserializing = false) 
    { 
     this._isDeserializing = isDeserializing; 
     this._serviceResponseBase = new ServiceResponseBase(); 
    } 

    public BankServiceResponse(Guid requestId, List<BankResponse> responses, Guid? asyncRequestId = null, Identifier name = null, Status status = null) 
    { 
     if (responses == null || responses.Count == 0) 
      throw new ArgumentNullException("The list cannot be null or empty", "responses"); 

     this._serviceResponseBase = new ServiceResponseBase(requestId, asyncRequestId, name, status); 
     this._responses = responses; 
    } 

    [XmlElement(Order = 1)] 
    public Status Status 
    { 
     get { return this._serviceResponseBase.Status; } 
     set { this._serviceResponseBase.Status = value; } 
    } 

    [XmlElement(Order = 2)] 
    public Guid RequestId 
    { 
     get { return this._serviceResponseBase.RequestId; } 
     set { this._serviceResponseBase.RequestId = value; } 
    } 

    [XmlElement(Order = 3)] 
    public Guid? AsyncRequestId 
    { 
     get { return this._serviceResponseBase.AsyncRequestId; } 
     set { this._serviceResponseBase.AsyncRequestId = value; } 
    } 

    [XmlIgnore] 
    public bool AsyncRequestIdSpecified 
    { 
     get { return this._serviceResponseBase.AsyncRequestIdSpecified; } 
     set { ; } // Must have this for XmlSerializer. 
    } 

    [XmlElement(Order = 4)] 
    public Identifer Name 
    { 
     get { return this._serviceResponseBase.Name; } 
     set { this._serviceResponseBase.Name; } 
    } 

    [XmlElement(Order = 5)] 
    public List<BankResponse> Responses 
    { 
     get { return this._responses; } 
     set 
     { 
      if (this._isDeserializing && this._responses != null && this._responses.Count > 0) 
       this._isDeserializing = false; 

      if (!this._isDeserializing && (value == null || value.Count == 0)) 
       throw new ArgumentNullException("List cannot be null or empty.", "value"); 

      this._responses = value; 
     } 
    } 
    private List<BankResponse> _responses; 
} 

तो, जबकि मैं निहित वर्गों के सभी के लिए गुण पैदा करने के लिए है, मैं मैं निहित वर्ग (ते) संपत्ति setters/टिककर खेल के भीतर हो सकता है किसी भी कस्टम तर्क बस निहित वर्ग के गुणों का उपयोग करते समय से प्रतिनिधि कर सकते हैं पत्ती वर्ग की संपत्तियों का उपयोग किया जाता है। चूंकि कोई विरासत नहीं है, इसलिए मैं XmlElementAttribute विशेषता के साथ पत्ती वर्ग के सभी गुणों को सजाने और किसी भी ऑर्डरिंग का उपयोग कर सकता हूं जो मैं फिट देखता हूं।


अद्यतन:

मैं वापस आया इस लेख फिर से क्योंकि वर्ग वंशानुक्रम का उपयोग के बारे में मेरे डिजाइन फैसले वापस मुझे फिर से काटने के लिए आया था। जबकि उपरोक्त मेरा समाधान काम करता है, मैं इसका उपयोग कर रहा हूं, मुझे सच में लगता है कि नाडर का समाधान सबसे अच्छा है और मैंने प्रस्तुत किए गए समाधान से पहले विचार किया जाना चाहिए। असल में, मैं आज उसे +1 कर रहा हूं! मुझे वास्तव में उसका जवाब पसंद है, और यदि मेरे पास कभी भी मेरी वर्तमान परियोजना को दोबारा करने का अवसर है, तो मैं निश्चित रूप से ऑब्जेक्ट्स के लिए क्रमबद्ध तर्क से व्यवसाय ऑब्जेक्ट को अलग कर दूंगा जो अन्यथा कोड को सरल बनाने और इसे आसान बनाने के लिए विरासत से बहुत लाभान्वित होगा दूसरों के उपयोग और समझने के लिए।

आपकी प्रतिक्रिया नाडर पोस्ट करने के लिए धन्यवाद, क्योंकि मुझे लगता है कि कई इसे बहुत ही निर्देशक और उपयोगी पाएंगे।

2

यह पोस्ट अब काफी पुराना है, लेकिन मुझे हाल ही में डब्लूसीएफ में एक ही समस्या थी, और स्टीव कूपर के ऊपर एक समाधान मिला, लेकिन जो काम करता है, और संभवतः एक्सएमएल सीरियलाइजेशन के लिए भी काम करेगा।

यदि आप बेस क्लास से XmlElement विशेषताओं को हटाते हैं, और प्रत्येक संपत्ति की एक प्रति को व्युत्पन्न कक्षाओं में एक अलग नाम से जोड़ते हैं जो प्राप्त/सेट के आधार पर आधार मूल्य तक पहुंचता है, तो प्रतियों को उचित नाम से क्रमबद्ध किया जा सकता है एक XmlElementAttribute का उपयोग कर सौंपा, और उम्मीद है कि उसके बाद डिफ़ॉल्ट क्रम में क्रमानुसार देगा:

public class SerializableBase 
{ 
    public bool Property1 { get; set;} 
    public bool Property3 { get; set;} 
} 

[XmlRoot("Object")] 
public class SerializableObject : SerializableBase 
{ 
    [XmlElement("Property1")] 
    public bool copyOfProperty1 
    { 
    get { return base.Property1; } 
    set { base.Property1 = value; } 
    } 

    [XmlElement] 
    public bool Property2 { get; set;} 

    [XmlElement("Property3")] 
    public bool copyOfProperty3 
    { 
    get { return base.Property3; } 
    set { base.Property3 = value; } 
    } 
} 

मैं भी व्युत्पन्न वर्ग में जोड़ने के लिए इतना है कि प्रतियां अनिवार्य बनाया जा सकता है एक अंतरफलक कहा:

interface ISerializableObjectEnsureProperties 
{ 
    bool copyOfProperty1 { get; set; } 
    bool copyOfProperty2 { get; set; } 
} 

यह ईएस नहीं है भावनात्मक लेकिन इसका मतलब है कि मैं परिणामस्वरूप एक्सएमएल की जांच करने के बजाय संकलन समय पर सबकुछ लागू कर सकता हूं। मैंने मूल रूप से SerializableBase के इन अमूर्त गुणों को बनाया था, लेकिन फिर यह पहले (बेस क्लास के साथ) क्रमबद्ध करता है, जिसे अब मुझे तार्किक लगता है।

यह ऊपर एक लाइन बदलकर हमेशा की तरह कहा जाता है:

public class SerializableObject : SerializableBase, ISerializableObjectEnsureProperties 

मैं केवल WCF में यह परीक्षण किया है, और संकलन के बिना एक्सएमएल क्रमबद्धता अवधारणा पोर्ट है, इसलिए यदि यह नहीं करता है काम, माफ़ी, लेकिन मुझे उम्मीद है कि यह वैसे ही व्यवहार करेगी - मुझे यकीन है कि कोई मुझे बताएगा अगर नहीं ...

2

मुझे पता है कि यह प्रश्न समाप्त हो गया है; हालांकि, यहां इस समस्या का समाधान है:

विधि का नाम हमेशा कंधेरियल के साथ शुरू होना चाहिए और फिर संपत्ति के नाम के साथ समाप्त होना चाहिए। फिर आपको मूल्यवान क्रमबद्ध करने के लिए या नहीं, इस शर्त के आधार पर आपको बस जो भी सशर्त इच्छा है, उसके आधार पर एक बूलियन वापस करने की आवश्यकता है।

public class SerializableBase 
{ 
    public bool Property1 { get; set;} 
    public bool Property2 { get; set;} 
    public bool Property3 { get; set;} 

    public virtual bool ShouldSerializeProperty2 { get { return false; } } 
} 

[XmlRoot("Object")] 
public class SerializableObject1 : SerializableBase 
{   
} 

[XmlRoot("Object")] 
public class SerializableObject2 : SerializableBase 
{ 
    public override bool ShouldSerializeProperty2 { get { return true; } } 
} 

परिणाम जब SerializableObject2 का उपयोग कर: ~

<Object> 
    <Property1></Property1> 
    <Property2></Property2> 
    <Property3></Property3> 
</Object> 

परिणाम जब SerializableObject1 का उपयोग कर: ~

<Object> 
    <Property1></Property1> 
    <Property3></Property3> 
</Object> 

आशा इस कई अन्य लोगों के मदद करता है!

+0

यह वास्तव में काम करता है। हालांकि, आपने अनिवार्य रूप से उन वर्गों में Property2 को जोड़ा है, जिनमें से संबंधित नहीं है (भले ही यह xml में दिखाई न दे)। – Ian1971

संबंधित मुद्दे