我有一个使用 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}", 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 并将 KnownType 属性添加到 DataEvent 将允许我保留类型 - 但它似乎不起作用。
编辑:如果我以 XML 格式向 SubmitDataEvents 发送请求(使用 Content-Type: text/xml 而不是 text/json ),那么 List<DataEvent> DataEvents 确实包含子类型而不是父类(super class)型。一旦我将请求设置为 text/json 并发送上述数据包,我就只能得到父类(super class)型,而不能将它们转换为子类型。我的 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 数据包时仍然不起作用。我只是得到一个包含 List 而不是 DataEvent 和 IntEvent 的 BoolEvent。
编辑 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 header 。
我已经用你的代码试过了,它工作得很好(好吧,我已经删除了 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 以及最新更新。
我接受你的服务契约(Contract)、实现和数据契约(Contract)。我将它们托管在 Cassini 下的 Web 应用程序中。我有以下 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",
"Id": 12345,
"Timestamp": "\/Date(1324905383689+0000)\/",
"Value": 5
}, {
"__type": "BoolEvent:#WebApplication1",
"Id": 45678,
"Timestamp": "\/Date(1324905383689+0000)\/",
"Value": true
}]
}
然后我在服务实现中有如下代码:
public void SubmitDataEvents(DataPacket parameter)
{
foreach (DataEvent dataEvent in parameter.DataEvents)
{
var message = dataEvent.ToString();
Debug.WriteLine(message);
}
}
请注意,调试器将项目详细信息显示为 DataEvent,但字符串表示和详细信息中的第一项清楚地表明所有子类型都已反序列化:

在我点击该方法后,调试输出包含以下内容:
IntEvent: 12345, 26.12.2011 20:16:23, 5
BoolEvent: 45678, 26.12.2011 20:16:23, True
我也试过在 IIS 下运行它(在 Win7 上),一切正常。
在我通过从 __type 字段名称中删除一个下划线来破坏数据包后,我只反序列化了基本类型。如果我修改 __type 的值,调用将在反序列化期间崩溃,它不会命中服务。
以下是您可以尝试的方法:
System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString()。它将包含从您的 JSON 翻译而来的 XML 消息。检查是否正确。__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 服务中保留多态类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8625803/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h