我有一个使用webHttpBinding端点的C#WCF服务,它将接收并返回 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}",Timestamp,Value);
    }
}

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

    public override string ToString()
    {
        return string.Format("BoolEvent: {0},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,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,"Value": true
    }]
}

对长篇文章抱歉,但是我可以做些什么来保护每个对象的基本类型?我以为添加类型提示到JSON和KNownType属性到DataEvent将允许我保留类型 – 但它似乎不起作用.

编辑:如果我将请求发送到XML格式的SubmitDataEvents(使用Content-Type:text / xml而不是text / json),则List< DataEvent> DataEvents确实包含子类型而不是超类型.一旦我将请求设置为text / json并发送上述数据包,那么我只得到超类型,我不能将它们转换为子类型.我的XML请求体是:

<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:更新服务描述之后Pavel的评论.在fiddler2中发送JSON数据包时仍然无效.我只是得到一个包含DataEvent而不是IntEvent和BoolEvent的List.

编辑3:正如Pavel所建议的,这是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>

该消息重复4次(以__type为单元,两次为Value).看起来类型提示信息被忽略,那么Value元素将被忽略,因为数据包反序列化为DataEvent而不是IntEvent / BoolEvent.

解决方法

无论何时处理序列化,尝试首先序列化一个对象图以查看序列化的字符串格式.然后使用格式来生成正确的序列化字符串.

您的数据包不正确正确的是:

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并在调试器中测试).所有的类层次结构都很好,所有对象都可以被转换为它们的类型.有用.

UPDATE

您发布的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

多么奇怪…它在我的机器上工作.

我使用.NET 4和VS2010与Win7 x64上的最新更新.

我接受您的服务合同,实施和数据合同.我在Cassini的网络应用程序中托管他们.我有以下web.config:

<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进行以下POST(重要的是:我已经将派生类型的命名空间重命名为符合我的情况):

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",{
        "__type": "BoolEvent:#WebApplication1","Value": true
    }]
}

然后我在服务实现中有以下代码:

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

请注意,调试器将项目详细信息显示为DataEvents,但字符串表示和细节中的第一个项目清楚地显示所有子类型已经反序列化:

并且调试输出包含以下内容后我打了方法:

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

我也尝试在IIS(在Win7下)运行它,一切都很好.

我通过从__type字段名称中删除一个下划线,在损坏数据包之后,只有反序列化的基类型.如果我修改__type的值,则在反序列化期间,调用将崩溃,它不会触发服务.

这是你可以尝试的:

>确保没有任何调试消息,异常等(检查调试输出).
>创建一个新的干净的Web应用程序解决方案,粘贴所需的代码并测试它是否在那里工作.如果是,那么您的原始项目必须有一些奇怪的配置设置.
>在调试器中,在Watch窗口中分析System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString().它将包含从您的JSON翻译的XML消息.检查是否正确.
>检查您是否有任何等待更新的.NET.
>尝试tracing WCF.虽然似乎没有发出任何警告的错误__type字段名称的消息,可能会发生,它会显示一些提示您的问题的原因.

我的请求消息

像这里似乎是这个问题的轨迹:当你有__type作为元素,我有它作为属性.假设您的WCF程序集在JSON到XML翻译中有错误

<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的地方.这里是:

// 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
    } 
  } 
}

我试图找到使用属性来确定反序列化类型的地方,但是没有运气.

希望这可以帮助.

c# – 使用JSON保留WCF服务中的多形态类型的更多相关文章

  1. 视频流 – 使用视频工具箱解码iOS 8中的h264

    需要解码h264流并获取像素缓冲区我知道iOS8上的视频工具箱可能1.如何将h264流转换为CMSampleBufferRef?

  2. ios – 访问文件属性与访问sqlite记录

    >看到上述结果后,我决定选择attributesOfItemAtPath方法.还有什么我不是考虑传递sqlite?

  3. ios – NSTimeInterval到unix时间戳

    我从cmmotionmanager获取CMDeviceMotion对象.CMDeviceMotion的一个属性是时间戳,表示为NSTimeInterval.根据文档,这允许“亚毫秒”时间戳精度.不幸的是,NSTimeInterval是自上次设备启动以来计算的,对以原始形式使用它提出了重大挑战.有没有人有一个工作代码将此NSTimeInterval转换为类似时间戳的Unix?解决方法在将磁力计值与CoreMotion事件进行比较时,我遇到了类似的问题.如果要转换这些NSTimeIntervals,您只需要计

  4. ios – 更改JSQMessagesViewController中的时间戳逻辑

    SOMessaging一天一天怎么样?

  5. 如何从iOS中的CMSampleBufferRef获取相机数据当前捕获的时间戳

    我开发和iOS应用程序,将捕获的相机数据保存到一个文件,我使用捕获CMSampleBufferRef,并将编码为H264格式,帧将使用AVAssetWriter保存到文件.我遵循示例源代码来创建此应用程序:http://www.gdcl.co.uk//2013/02/20/iOS-Video-Encoding.html现在我想获得保存的视频帧的时间戳来创建一个新的电影文件,为此我做了以下事情1)找

  6. ios – CGPath和UIBezierPath()有什么区别?

    目前,我正在努力制作一个自定义按钮,我有一个图像,并具有坐标,但我发现您可以通过使用CGPath类或UIBezierPath创建一个按钮/对象类.有人可以告诉我两者有什么区别?解决方法CGPath是CoreGraphics库的不透明类型,而UIBezierPath是UIKit中的Obj-C类.UIBezierPath是一个围绕CGPath的包装,具有更加面向对象的界面和一些方便的方法.使用CGPath可能会略微更快,因为它不必经过Obj-C,并且它具有更高级的功能,如CGPathApply.重要的是,UI

  7. ios – 如何维护两个不同设备之间的时钟会话?

    我正在研究iOS应用程序,它需要在接受两个设备用户时在两个设备之间维持时钟计时器会话?但我不确定如何在两台设备上都没有时间缺陷的情况下实现这一目标?

  8. Swift开发用到的一些工具类

  9. Swift&amp;Cocoa实战之数据类型:时间日期

    时间日期NSDate在Objective-C中,可以使用如下的代码创建一个UTC的时间:但是在Swift中,如果使用如下方式:获取到的会是本地时间。

  10. swift 快速奔跑的兔几 本节的内容是:序列化与反序列化

    在cocoa中,我们经常需要向磁盘保存数据块,cocoa将这些数据块表示为NSData对象例如,有一个字符串,将其转换为NSData,可以使用如下方法:我们还可以将对象转化为数据。遵守协议NSCoding的对象可以转换为NSData对象,也可以从NSData对象中加载,方法如下:

随机推荐

  1. c# – (wpf)Application.Current.Resources vs FindResource

    所以,我正在使用C#中的WPF创建一个GUI.它看起来像这样:它现在还没有完成.这两行是我尝试制作一种数据表,它们在XAML中是硬编码的.现在,我正在C#中实现添加新的水果按钮功能.我在XAML中有以下样式来控制行的背景图像应该是什么样子:因此,在代码中,我为每列col0,col1和col2创建一个图像,如果我使用以下代码,它添加了一个如下所示的新行:如你所见,它不太正确……为什么一个似乎忽略了一些属性而另一个没有?

  2. c# – 绑定DataGridTemplateColumn

    似乎我已经打了个墙,试图在DataGrid上使用DataTemplates.我想要做的是使用一个模板来显示每个单元格的两行文本.但是似乎无法以任何方式绑定列.以下代码希望显示我想做的事情.注意每个列的绑定:模板列没有这样的东西,因此,这个xaml不可能工作.我注定要将整个DataTemplate复制到每个列,只是对每个副本都有不同的约束?解决方法我不完全确定你想要做什么,但如果您需要获取整行的DataContext,可以使用RelativeSource绑定来移动视觉树.像这样:

  3. c# – 学习设计模式的资源

    最近我来到了这个设计模式的概念,并对此感到非常热情.你能建议一些帮助我深入设计模式的资源吗?

  4. c# – 是否有支持嵌入HTML页面的跨操作系统GUI框架?

    我想开发一个桌面应用程序来使用跨系统,是否有一个GUI框架,允许我为所有3个平台编写一次代码,并具有完全可脚本化的嵌入式Web组件?我需要它有一个API来在应用程序和网页之间进行交流.我知道C#,JavaScript和一些python.解决方法Qt有这样的事情QWebView.

  5. c# – 通过字符串在对象图中查找属性

    我试图使用任意字符串访问嵌套类结构的各个部分.给出以下(设计的)类:我想要从Person对象的一个实例的“PersonsAddress.HousePhone.Number”获取对象.目前我正在使用反思来做一些简单的递归查找,但是我希望有一些忍者有更好的想法.作为参考,这里是我开发的(crappy)方法:解决方法您可以简单地使用标准的.NETDataBinder.EvalMethod,像这样:

  6. c# – 文件下载后更新页面

    FamilyID=0a391abd-25c1-4fc0-919f-b21f31ab88b7&displaylang=en&pf=true它呈现该页面,然后使用以下元刷新标签来实际向用户提供要下载的文件:你可能需要在你的应用程序中做类似的事情.但是,如果您真的有兴趣在文件完全下载后执行某些操作,那么您的运气不佳,因为没有任何事件可以与浏览器进行通信.执行此操作的唯一方法是上传附件时使用的AJAXupload.

  7. c# – 如何在每个机器应用程序中实现单个实例?

    我必须限制我的.net4WPF应用程序,以便每台机器只能运行一次.请注意,我说每个机器,而不是每个会话.我使用一个简单的互斥体实现单实例应用程序,直到现在,但不幸的是,这样一个互斥是每个会话.有没有办法创建机器互连,还是有其他解决方案来实现每个机器应用程序的单个实例?

  8. c# – WCF和多个主机头

    我的雇主网站有多个主机名,都是同一个服务器,我们只是显示不同的皮肤来进行品牌宣传.不幸的是,在这种情况下,WCF似乎不能很好地工作.我试过overridingthedefaulthostwithacustomhostfactory.这不是一个可以接受的解决方案,因为它需要从所有主机工作,而不仅仅是1.我也看过thisblogpost,但是我无法让它工作,或者不是为了解决我的问题.我得到的错误是“这

  9. c# – ASP.NET MVC模型绑定与表单元素名称中的虚线

    我一直在搜索互联网,试图找到一种方式来容纳我的表单元素的破折号到ASP.NET的控制器在MVC2,3或甚至4中的默认模型绑定行为.作为一名前端开发人员,我更喜欢在我的CSS中使用camelCase或下划线进行破折号.在我的标记中,我想要做的是这样的:在控制器中,我会传入一个C#对象,看起来像这样:有没有办法通过一些正则表达式或其他行为来扩展Controller类来适应这种情况?我讨厌这样的事实,我必须这样做:甚至这个:思考?

  10. c# – 用户界面设计工具

    我正在寻找一个用户界面设计工具来显示文档中可能的GUI.我不能生成代码.我知道MicrosoftVisio提供了一个功能.但有什么办法吗?您使用哪种软件可视化GUI?

返回
顶部