JSON

2011-12-24 15 views
11

का उपयोग कर डब्ल्यूसीएफ सेवा में पॉलीमोर्फिक प्रकारों को संरक्षित करना मेरे पास एक वेब एचटीएफबी सेवा का उपयोग कर सी # डब्ल्यूसीएफ सेवा है जो जेएसओएन प्रारूप में डेटा प्राप्त और वापस कर देगी। भेजने/प्राप्त करने के लिए डेटा को पॉलिमॉर्फिक प्रकार का उपयोग करने की आवश्यकता होती है ताकि विभिन्न प्रकार के डेटा को उसी "डेटा पैकेट" में आदान-प्रदान किया जा सके। मैं निम्न डेटा मॉडल है:JSON

[DataContract] 
public class DataPacket 
{ 
    [DataMember] 
    public List<DataEvent> DataEvents { get; set; } 
} 

[DataContract] 
[KnownType(typeof(IntEvent))] 
[KnownType(typeof(BoolEvent))] 
public class DataEvent 
{ 
    [DataMember] 
    public ulong Id { get; set; } 

    [DataMember] 
    public DateTime Timestamp { get; set; } 

    public override string ToString() 
    { 
     return string.Format("DataEvent: {0}, {1}", Id, Timestamp); 
    } 
} 

[DataContract] 
public class IntEvent : DataEvent 
{ 
    [DataMember] 
    public int Value { get; set; } 

    public override string ToString() 
    { 
     return string.Format("IntEvent: {0}, {1}, {2}", Id, Timestamp, Value); 
    } 
} 

[DataContract] 
public class BoolEvent : DataEvent 
{ 
    [DataMember] 
    public bool Value { get; set; } 

    public override string ToString() 
    { 
     return string.Format("BoolEvent: {0}, {1}, {2}", Id, Timestamp, Value); 
    } 
} 

मेरी सेवा भेज देंगे, उप-प्रकार की घटनाओं (IntEvent, BoolEvent आदि) एक भी डाटा पैकेट में प्राप्त/इस प्रकार है:

[ServiceContract] 
public interface IDataService 
{ 
    [OperationContract] 
    [WebGet(UriTemplate = "GetExampleDataEvents")] 
    DataPacket GetExampleDataEvents(); 

    [OperationContract] 
    [WebInvoke(UriTemplate = "SubmitDataEvents", RequestFormat = WebMessageFormat.Json)] 
    void SubmitDataEvents(DataPacket dataPacket); 
} 

public class DataService : IDataService 
{ 
    public DataPacket GetExampleDataEvents() 
    { 
     return new DataPacket { 
      DataEvents = new List<DataEvent> 
      { 
       new IntEvent { Id = 12345, Timestamp = DateTime.Now, Value = 5 }, 
       new BoolEvent { Id = 45678, Timestamp = DateTime.Now, Value = true } 
      } 
     }; 
    } 

    public void SubmitDataEvents(DataPacket dataPacket) 
    { 
     int i = dataPacket.DataEvents.Count; //dataPacket contains 2 events, but both are type DataEvent instead of IntEvent and BoolEvent 
     IntEvent intEvent = dataPacket.DataEvents[0] as IntEvent; 
     Console.WriteLine(intEvent.Value); //null pointer as intEvent is null since the cast failed 
    } 
} 

जब मैं मेरे पैकेट को SubmitDataEvents विधि पर सबमिट करें, हालांकि मुझे DataEvent प्रकार मिलते हैं और उन्हें InvalidCastException में परिणामों के आधार पर वापस अपने मूल प्रकार (केवल परीक्षण उद्देश्यों के लिए) कास्ट करने का प्रयास करते हैं। मेरे पैकेट है: लंबे समय तक इस पद के लिए

POST http://localhost:4965/DataService.svc/SubmitDataEvents HTTP/1.1 
User-Agent: Fiddler 
Host: localhost:4965 
Content-Type: text/json 
Content-Length: 340 

{ 
    "DataEvents": [{ 
     "__type": "IntEvent:#WcfTest.Data", 
     "Id": 12345, 
     "Timestamp": "\/Date(1324905383689+0000)\/", 
     "Value": 5 
    }, { 
     "__type": "BoolEvent:#WcfTest.Data", 
     "Id": 45678, 
     "Timestamp": "\/Date(1324905383689+0000)\/", 
     "Value": true 
    }] 
} 

क्षमा याचना, लेकिन वहाँ कुछ भी मैं प्रत्येक वस्तु के आधार प्रकार संरक्षित करने के लिए कर सकते हैं? मैंने सोचा कि JSON के लिए संकेत संकेत जोड़ना और ज्ञात टाइप प्रकार DataEvent पर मुझे प्रकारों को संरक्षित करने की अनुमति होगी - लेकिन ऐसा लगता है कि यह काम नहीं करता है।

संपादित: यदि मैं अनुरोध SubmitDataEvents को XML स्वरूप में (Content-Type: text/xml बजाय text/json के साथ) भेजने तो List<DataEvent> DataEvents सुपर प्रकार के बजाय उप-प्रकारों को शामिल करता है। जैसे ही मैंने text/json पर अनुरोध सेट किया है और उपरोक्त पैकेट भेज दिया है, तो मुझे केवल सुपर-टाइप मिलता है और मैं उन्हें उप-प्रकार में नहीं डाल सकता। मेरे एक्सएमएल अनुरोध शरीर है:

<ArrayOfDataEvent xmlns="http://schemas.datacontract.org/2004/07/WcfTest.Data"> 
    <DataEvent i:type="IntEvent" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <Id>12345</Id> 
    <Timestamp>1999-05-31T11:20:00</Timestamp> 
    <Value>5</Value> 
    </DataEvent> 
    <DataEvent i:type="BoolEvent" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <Id>56789</Id> 
    <Timestamp>1999-05-31T11:20:00</Timestamp> 
    <Value>true</Value> 
    </DataEvent> 
</ArrayOfDataEvent> 

संपादित 2: नीचे पावेल की टिप्पणी के बाद अपडेट किया गया सेवा विवरण। Fiddler2 में JSON पैकेट भेजते समय यह अभी भी काम नहीं करता है। मुझे ListIntEvent और BoolEvent के बजाय DataEvent मिला है।

संपादित करें 3: जैसा कि पावेल ने सुझाव दिया है, यहां System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString() से आउटपुट है। मुझे ठीक लग रहा है।

<root type="object"> 
    <DataEvents type="array"> 
     <item type="object"> 
      <__type type="string">IntEvent:#WcfTest.Data</__type> 
      <Id type="number">12345</Id> 
      <Timestamp type="string">/Date(1324905383689+0000)/</Timestamp> 
      <Value type="number">5</Value> 
     </item> 
     <item type="object"> 
      <__type type="string">BoolEvent:#WcfTest.Data</__type> 
      <Id type="number">45678</Id> 
      <Timestamp type="string">/Date(1324905383689+0000)/</Timestamp> 
      <Value type="boolean">true</Value> 
     </item> 
    </DataEvents> 
</root> 

जब पैकेट के अक्रमांकन अनुरेखण, मैं पता लगाने में निम्न संदेश मिलता है:

<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Verbose"> 
    <TraceIdentifier>http://msdn.microsoft.com/en-GB/library/System.Runtime.Serialization.ElementIgnored.aspx</TraceIdentifier> 
    <Description>An unrecognized element was encountered in the XML during deserialization which was ignored.</Description> 
    <AppDomain>1c7ccc3b-4-129695001952729398</AppDomain> 
    <ExtendedData xmlns="http://schemas.microsoft.com/2006/08/ServiceModel/StringTraceRecord"> 
     <Element>:__type</Element> 
    </ExtendedData> 
</TraceRecord> 

यह संदेश दोहराया है (दो बार __type तत्व के रूप में और दो बार Value साथ साथ) 4 बार। ऐसा लगता है कि टाइपिंग जानकारी को अनदेखा किया जा रहा है, तो Value तत्वों को अनदेखा कर दिया गया है क्योंकि IntEvent/BoolEvent के बजाय पैकेट को DataEvent पर deserialized किया गया है।

उत्तर

2

पावेल गैतिलोव के लिए धन्यवाद, अब मुझे इस समस्या का समाधान मिला है। मैं इसे किसी भी ऐसे व्यक्ति के लिए अलग उत्तर के रूप में जोड़ूंगा जो भविष्य में इस द्वारा पकड़ा जा सकता है।

समस्या यह है कि JSON deserializer सफेद जगहों को बहुत स्वीकार नहीं कर रहा है। मैं जो पैकेट भेज रहा था, उसमें डेटा को और अधिक पठनीय बनाने के लिए लाइन ब्रेक और रिक्त स्थान के साथ "सुंदर मुद्रित" था। हालांकि, जब यह पैकेट deserialized था, इसका मतलब था कि "__type" संकेत की तलाश करते समय, JSON deserializer पैकेट के गलत हिस्से को देख रहा था। इसका मतलब था कि प्रकार का संकेत याद किया गया था और पैकेट गलत प्रकार के रूप में deserialized था।

निम्नलिखित पैकेट सही ढंग से काम करता है:

POST http://localhost:6463/DataService.svc/SubmitDataEvents HTTP/1.1 
User-Agent: Fiddler 
Content-Type: text/json 
Host: localhost:6463 
Content-Length: 233 

{"DataEvents":[{"__type":"IntEvent:#WebApplication1","Id":12345,"Timestamp":"\/Date(1324905383689+0000)\/","IntValue":5},{"__type":"BoolEvent:#WebApplication1","Id":45678,"Timestamp":"\/Date(1324905383689+0000)\/","BoolValue":true}]} 

हालांकि, इस पैकेट काम नहीं करता:

POST http://localhost:6463/DataService.svc/SubmitDataEvents HTTP/1.1 
User-Agent: Fiddler 
Content-Type: text/json 
Host: localhost:6463 
Content-Length: 343 

{ 
    "DataEvents": [{ 
     "__type": "IntEvent:#WebApplication1", 
     "Id": 12345, 
     "Timestamp": "\/Date(1324905383689+0000)\/", 
     "IntValue": 5 
    }, { 
     "__type": "BoolEvent:#WebApplication1", 
     "Id": 45678, 
     "Timestamp": "\/Date(1324905383689+0000)\/", 
     "BoolValue": true 
    }] 
} 

ये पैकेट वास्तव में लाइन टूट जाता है और रिक्त स्थान से अलग ही हैं।

+0

वाह ... आपकी समस्या को जानकर खुशी हुई है।हालांकि, स्वरूपित जेएसओएन ने मेरे लिए ठीक काम किया है, मैंने हमेशा इसे फिडलर को कॉपी-पेस्ट किया है और इसमें कोई समस्या नहीं है। मुझे विश्वास है कि या तो आपने .NET ढांचे के लिए सभी अद्यतन स्थापित नहीं किए हैं, या नवीनतम संस्करण में व्हाइटस्पेस वर्ण प्रसंस्करण से संबंधित एक अजीब बग है। मैं पूरी तरह से शर्मिंदा हूँ। –

+0

बिल्कुल शर्मिंदा मत हो, मैं आपकी सहायता के बिना कभी नहीं पाया होगा। आपके द्वारा पता चला एकमात्र तरीका डब्ल्यूसीएफ स्रोत कोड को डीबग करके था जिसे आपने ऊपर पोस्ट किया था। मैंने देखा कि 'ऑफ़सेट' के लिए मान हमेशा बहुत कम था और यह नोटिस हुआ कि संदेश बफर में '\ r' और' \ n' वर्णों को शामिल करना ऑफसेट गणना को किसी भी तरह से फेंक रहा था। मैं यह देखने के लिए एक और सवाल पोस्ट कर सकता हूं कि क्या कोई जानता है कि व्हाइटस्पेस पार्सिंग में हस्तक्षेप क्यों करता है, क्योंकि मुझे इसमें शामिल होने की लचीलापन चाहिए या नहीं। –

+1

ओह, यह 'शर्मिंदा' के बजाय 'भ्रमित' होना चाहिए था :)। वैसे, मुझे इसी तरह के मुद्दे का विवरण मिला है [इस आलेख में] (http://blog.js-development.com/2010/05/n-will-break-your-json-jquery-wcf.html)। –

3

जब भी धारावाहिकता से निपटने के लिए, धारावाहिक स्ट्रिंग प्रारूप को देखने के लिए पहले ऑब्जेक्ट ग्राफ़ को क्रमबद्ध करने का प्रयास करें। फिर सही क्रमबद्ध तार बनाने के लिए प्रारूप का उपयोग करें।

आपका पैकेट गलत है। सही एक है:

POST http://localhost:47440/Service1.svc/SubmitDataEvents HTTP/1.1 
User-Agent: Fiddler 
Host: localhost:47440 
Content-Length: 211 
Content-Type: text/json 

[ 
    { 
    "__type":"IntEvent:#WcfTest.Data", 
    "Id":12345, 
    "Timestamp":"\/Date(1324757832735+0700)\/", 
    "Value":5 
    }, 
    { 
    "__type":"BoolEvent:#WcfTest.Data", 
    "Id":45678, 
    "Timestamp":"\/Date(1324757832736+0700)\/", 
    "Value":true 
    } 
] 

नोट Content-Type हैडर भी।

मैंने इसे आपके कोड से आजमाया है और यह पूरी तरह से काम करता है (ठीक है, मैंने Console.WriteLine हटा दिया है और डीबगर में परीक्षण किया है)। सभी वर्ग पदानुक्रम ठीक है, सभी वस्तुओं को उनके प्रकारों में डाला जा सकता है। यह काम करता हैं।

अद्यतन

JSON आप निम्न कोड के साथ काम करता है पोस्ट किया है:

[DataContract] 
public class SomeClass 
{ 
    [DataMember] 
    public List<DataEvent> dataEvents { get; set; } 
} 

... 

[ServiceContract] 
public interface IDataService 
{ 
    ... 

    [OperationContract] 
    [WebInvoke(UriTemplate = "SubmitDataEvents")] 
    void SubmitDataEvents(SomeClass parameter); 
} 

ध्यान दें कि एक और उच्च स्तरीय नोड वस्तु पेड़ में जोड़ा जाता है।

और फिर, यह विरासत के साथ ठीक काम करता है।

यदि समस्या अभी भी बनी हुई है, तो कृपया उस कोड को पोस्ट करें जिसका उपयोग आप सेवा को आमंत्रित करने के लिए करते हैं, साथ ही अपवाद विवरण भी प्राप्त करते हैं।

अद्यतन 2

कैसे अजीब ... यह मेरे मशीन पर काम करता है।

मैं Win7 x64 पर नवीनतम अपडेट के साथ .NET 4 और VS2010 का उपयोग करता हूं।

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

POST http://localhost:47440/Service1.svc/SubmitDataEvents HTTP/1.1 
User-Agent: Fiddler 
Content-Type: text/json 
Host: localhost:47440 
Content-Length: 336 

{ 
    "DataEvents": [{ 
     "__type": "IntEvent:#WebApplication1", 
     "Id": 12345, 
     "Timestamp": "\/Date(1324905383689+0000)\/", 
     "Value": 5 
    }, { 
     "__type": "BoolEvent:#WebApplication1", 
     "Id": 45678, 
     "Timestamp": "\/Date(1324905383689+0000)\/", 
     "Value": true 
    }] 
} 

:

<configuration> 
    <connectionStrings> 
    <!-- excluded for brevity --> 
    </connectionStrings> 

    <system.web> 
    <!-- excluded for brevity --> 
    </system.web> 

    <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"/> 
    </system.webServer> 
    <system.serviceModel> 
    <behaviors> 
     <serviceBehaviors> 
     <behavior name=""> 
      <serviceMetadata httpGetEnabled="true" /> 
      <serviceDebug includeExceptionDetailInFaults="false" /> 
     </behavior> 
     </serviceBehaviors> 
     <endpointBehaviors> 
     <behavior name="WebBehavior"> 
      <webHttp /> 
     </behavior> 
     </endpointBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> 
    <services> 
     <service name="WebApplication1.DataService"> 
     <endpoint address="ws" binding="wsHttpBinding" contract="WebApplication1.IDataService"/> 
     <endpoint address="" behaviorConfiguration="WebBehavior" 
      binding="webHttpBinding" 
      contract="WebApplication1.IDataService"> 
     </endpoint> 
     <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 
     </service> 
    </services> 
    </system.serviceModel> 
</configuration> 

अब मैं Fiddler2 (: मैं अपने मामले मैच के लिए ली गई प्रकार के नाम स्थान का नाम बदला गया है महत्वपूर्ण) द्वारा निम्न पोस्ट बनाने: मैं निम्नलिखित web.config है मैं सेवा कार्यान्वयन में निम्नलिखित कोड है:

public void SubmitDataEvents(DataPacket parameter) 
{ 
    foreach (DataEvent dataEvent in parameter.DataEvents) 
    { 
    var message = dataEvent.ToString(); 
    Debug.WriteLine(message); 
    } 
} 

ध्यान दें कि डिबगर DataEvent रों के रूप में आइटम विवरण से पता चलता है, लेकिन स्ट्रिंग अभ्यावेदन और विस्तार से पहले आइटम रों स्पष्ट रूप से पता चलता है कि सभी उप-प्रकार अच्छी तरह से deserialized किया गया है:

IntEvent: 12345, 26.12.2011 20:16:23, 5 
BoolEvent: 45678, 26.12.2011 20:16:23, True 

मैं भी आईआईएस के तहत इसे चलाने की कोशिश की है (पर: Debugger screenshot

और डिबग आउटपुट मैं विधि हिट के बाद निम्नलिखित शामिल विन 7) और सब ठीक काम करता है।

__type फ़ील्ड नाम से एक अंडरस्कोर को हटाकर मैंने पैकेट को दूषित करने के बाद केवल मूल प्रकार का deserialized किया है। यदि मैं __type के मान को संशोधित करता हूं, तो कॉल deserialization के दौरान क्रैश हो जाएगा, यह सेवा को हिट नहीं करेगा।

  1. सुनिश्चित करें कि आप किसी भी डिबग संदेशों, अपवाद, आदि न करना पड़े (डिबग आउटपुट की जाँच):

    यहाँ आप क्या कोशिश कर सकते है।

  2. नया क्लीन वेब एप्लिकेशन समाधान बनाएं, आवश्यक कोड पेस्ट करें और परीक्षण करें यदि यह वहां काम करता है। यदि ऐसा होता है, तो आपके मूल प्रोजेक्ट में कुछ अजीब कॉन्फ़िगरेशन सेटिंग्स होनी चाहिए।
  3. डीबगर में, वॉच विंडो में System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString() का विश्लेषण करें।इसमें आपके JSON से अनुवादित XML संदेश होगा। जांचें कि यह सही है या नहीं।
  4. जांचें कि क्या आपके पास .NET के लिए कोई लंबित अपडेट है।
  5. tracing WCF आज़माएं। हालांकि यह गलत __type फ़ील्ड नाम वाले संदेशों के लिए किसी भी चेतावनी को उत्सर्जित नहीं करता है, ऐसा हो सकता है कि यह आपको आपके मुद्दों के कारणों के लिए कुछ संकेत दिखाएगा।

मेरे RequestMessage

की तरह लगता है यहाँ मुद्दा का ट्रैक है: जब तुम __type तत्व के रूप में है, मैं यह विशेषता के रूप में की है। माना जाता है, अपने WCF विधानसभाओं एक्सएमएल अनुवाद

<root type="object"> 
    <DataEvents type="array"> 
    <item type="object" __type="IntEvent:#WebApplication1"> 
     <Id type="number">12345</Id> 
     <Timestamp type="string">/Date(1324905383689+0000)/</Timestamp> 
     <Value type="number">5</Value> 
    </item> 
    <item type="object" __type="BoolEvent:#WebApplication1"> 
     <Id type="number">45678</Id> 
     <Timestamp type="string">/Date(1324905383689+0000)/</Timestamp> 
     <Value type="boolean">true</Value> 
    </item> 
    </DataEvents> 
</root> 

मैं जगह है जहाँ __type संसाधित किया जाता है मिल गया है करने के लिए JSON में एक बग है। संदेश यह है:

// from System.Runtime.Serialization.Json.XmlJsonReader, System.Runtime.Serialization, Version=4.0.0.0 
void ReadServerTypeAttribute(bool consumedObjectChar) 
{ 
    int offset; 
    int offsetMax; 
    int correction = consumedObjectChar ? -1 : 0; 
    byte[] buffer = BufferReader.GetBuffer(9 + correction, out offset, out offsetMax); 
    if (offset + 9 + correction <= offsetMax) 
    { 
    if (buffer[offset + correction + 1] == (byte) '\"' && 
     buffer[offset + correction + 2] == (byte) '_' && 
     buffer[offset + correction + 3] == (byte) '_' && 
     buffer[offset + correction + 4] == (byte) 't' && 
     buffer[offset + correction + 5] == (byte) 'y' && 
     buffer[offset + correction + 6] == (byte) 'p' && 
     buffer[offset + correction + 7] == (byte) 'e' && 
     buffer[offset + correction + 8] == (byte) '\"') 
    { 
     // It's attribute! 
     XmlAttributeNode attribute = AddAttribute(); 
     // the rest is omitted for brevity 
    } 
    } 
} 

मैं कोई भाग्य के लिए जगह है जहाँ विशेषता deserialized प्रकार निर्धारित करने के लिए किया जाता है लगता है की कोशिश की है, लेकिन।

उम्मीद है कि इससे मदद मिलती है।

+0

मैं जिस पैकेट को भेज रहा था उसे 'GetExampleDataEvents()' पहले कॉल करके उत्पन्न किया गया था, फिर JSON में 'dataEvents' में नाम बदलकर। मैंने HTTP शीर्षलेखों को शामिल नहीं किया था, लेकिन मैंने आपके द्वारा किया और किया था जैसे कि फिडलर 2 ('सामग्री-प्रकार: टेक्स्ट/जेसन'' का उपयोग करके पैकेट में भेजा गया। –

+0

यह बहुत अजीब बात है, क्योंकि मैंने जो JSON पोस्ट किया है वह ** बिल्कुल है ** JSON किसी भी संशोधन के बिना 'GetExampleDataEvents()' ** द्वारा लौटाया गया ** ** क्या आपने इसे आजमाया है? क्या यह काम करता है? अगर ऐसा होता है, तो आप इसे बदलने की कोशिश क्यों कर रहे हैं? जब मैंने आपके JSON को आजमाया, तो मुझे खाली सूची मिली , क्योंकि इसमें एक और कक्षा की आवश्यकता है जिसमें सूची होगी। –

+0

मैंने ऊपर अपनी सिफारिशों के साथ समस्या विवरण अपडेट किया है। फिर भी कोई भाग्य मुझे डर नहीं है! –