I've seen this kind of thing described in various examples showing how to create a REST service which takes arrays or a list of objects as part of the URL.
My question is, how to implement this using RESTeasy?
Something like the following would be how i would assume this to work.
#GET
#Path("/stuff/")
#Produces("application/json")
public StuffResponse getStuffByThings(
#QueryParam("things") List<Thing> things);
Create a StringConverter and a use a wrapper object. Here is a quick and dirty example:
public class QueryParamAsListTest {
public static class Thing {
String value;
Thing(String value){ this.value = value; }
}
public static class ManyThings {
List<Thing> things = new ArrayList<Thing>();
ManyThings(String values){
for(String value : values.split(",")){
things.add(new Thing(value));
}
}
}
static class Converter implements StringConverter<ManyThings> {
public ManyThings fromString(String str) {
return new ManyThings(str);
}
public String toString(ManyThings value) {
//TODO: implement
return value.toString();
}
}
#Path("/")
public static class Service {
#GET
#Path("/stuff/")
public int getStuffByThings(
#QueryParam("things") ManyThings things){
return things.things.size();
}
}
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getProviderFactory().addStringConverter(new Converter());
dispatcher.getRegistry().addSingletonResource(new Service());
MockHttpRequest request = MockHttpRequest.get("/stuff?things=a,b,c");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals("3", response.getContentAsString());
}
}
I think you can also use a StringParamUnmarshaller
I had some luck with this, using Collection rather than List. I was unable to make a StringConverter for List work.
#Provider
public class CollectionConverter implements StringConverter<Collection<String>> {
public Collection<String> fromString(String string) {
if (string == null) {
return Collections.emptyList();
}
return Arrays.asList(string.split(","));
}
public String toString(Collection<String> values) {
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (String value : values) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(value);
}
return sb.toString();
}
}
I did the toString from my head. Be sure to write unit tests for it to verify. But of course, everything is easier and clearer when you use Guava. Can use Joiner and Splitter. Really handy.
Just use a wrapper on its own, no need for anything else.
In your endpoint
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Path("/find")
#GET
MyResponse find(#QueryParam("ids") Wrapper ids);
And you wrapper looks like this :
public class Wrapper implements Serializable {
private List<BigInteger> ids = Collections.emptyList();
public String toString() {
return Joiner.on(",")
.join(ids);
}
public List<BigInteger> get() {
return ids;
}
public Wrapper(String s) {
if (s == null) {
ids = Collections.emptyList();
}
Iterable<String> splitted = Splitter.on(',')
.split(s);
Iterable<BigInteger> ids = Iterables.transform(splitted, Functionz.stringToBigInteger);
this.ids = Lists.newArrayList(ids);
}
public Wrapper(List<BigInteger> ids) {
this.ids = ids;
}
}
Related
I am using a #BeanParam like this:
#GET
public Response listAllPaged(#BeanParam PagedRequest pagedRequest) {
// Do stuff...
}
The bean itself:
public class PagedRequest {
#QueryParam("sortOrder")
#DefaultValue("0")
public int sortOrder;
}
Now I would like to change the type of sortOrder to the following enum:
public enum SortOrder {
ASC("asc"),
DESC("desc");
public final String sortOrder;
SortOrder(String sortOrder) {
this.sortOrder = sortOrder;
}
}
But as soon as I do this:
public class PagedRequest {
#QueryParam("sortOrder")
#DefaultValue("asc")
public SortOrder sortOrder;
}
My REST Endpoint cannot match the signature anymore and returns a 404. Why is that? I thought that the presence of a constructor accepting a single String should allow JAX-RS to do the conversion.
What am I doing wrong?
UPDATE
I managed to make it work like this, but it does not really answer my initial question...
public enum SortOrder {
ASC,
DESC;
public static SortOrder fromString(String param) {
String toUpper = param.toUpperCase();
try {
return valueOf(toUpper);
} catch (Exception e) {
return null;
}
}
}
The Enum.valueOf(String) method is used to resolve the value. Since your SorterOrder enums are uppercase you'd be required to send the parameter in uppercase.
If you want to pass the value in lowercase only you could change the enum names to lower case, e.g. SortOrder.asc.
If you don't know or don't want to care about the case the parameter is sent in you could use a ParamConverter.
public class SortOrderParamConverter implements ParamConverter<SortOrder> {
#Override
public SortOrder fromString(final String value) {
if (value != null) {
return SortOrder.valueOf(value.toUpperCase(Locale.ROOT));
}
return SortOrder.ASC;
}
#Override
public String toString(final SortOrder value) {
return value.name();
}
}
If you want a more generic approach you could create a ParamConverter or all enums.
#Provider
public class EnumParamConverterProvider implements ParamConverterProvider {
#Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType,
final Annotation[] annotations) {
if (!rawType.isEnum()) {
return null;
}
final Enum<?>[] constants = (Enum<?>[]) rawType.getEnumConstants();
return new ParamConverter<T>() {
#Override
#SuppressWarnings("unchecked")
public T fromString(final String value) {
if (value == null || value.isEmpty()) {
return null;
}
for (Enum<?> e : constants) {
if (e.name().equalsIgnoreCase(value)) {
return (T) e;
}
}
// No match, check toString()
for (Enum<?> e : constants) {
if (e.toString().equalsIgnoreCase(value)) {
return (T) e;
}
}
return null;
}
#Override
public String toString(final T value) {
return value != null ? value.toString() : null;
}
};
}
}
What is the cleanest way to map a string column to a Uri property using Dapper?
Here's the cleanest I've been able to come up with so far (using the ITypeMap functionality):
Query:
SELECT * FROM TableWithAStringAddressColumn
POCO:
public class MyPoco
{
[ColumnSetter("DapperAddress")]
public Uri Address { get; set; }
private string DapperAddress { set { this.Address = new Uri(value); } }
}
Extensions:
partial class SqlMapper
{
public static void InitializeTypeMaps()
{
SqlMapper.SetTypeMap(
typeof(MyPoco),
new CustomPropertyTypeMap(typeof(MyPoco), SqlMapper.CustomSetterMapper));
// call out every other class that needs this kind of mapping
}
public static Func<Type, string, PropertyInfo> CustomSetterMapper =
(type, columnName) =>
{
PropertyInfo prop = type
.GetProperties()
.FirstOrDefault(p => string.Equals(columnName, p.Name, StringComparison.OrdinalIgnoreCase));
if (prop != null)
{
// find out if we need to use a different setter
ColumnSetterAttribute setterAttribute = prop.GetCustomAttributes(false).OfType<ColumnSetterAttribute>().LastOrDefault();
if (setterAttribute != null)
{
PropertyInfo setterProp = type
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(p => string.Equals(setterAttribute.Setter, p.Name, StringComparison.OrdinalIgnoreCase));
if (setterProp == null)
{
throw new InvalidOperationException(string.Format("Setter property misconfigured (Property={0}, Setter={1})", prop.Name, setterAttribute.Setter));
}
else
{
prop = setterProp;
}
}
}
return prop;
};
}
Custom Attribute:
public class ColumnSetterAttribute : Attribute
{
public string Setter { get; set; }
public ColumnSetterAttribute(string setter)
{
this.Setter = setter;
}
}
[edit] I'm looking for a solution I can use without needing to call out all columns in all my queries (I'd like to find a solution where I can use SELECT *).
Seems like a lot of work...
Wouldn't this be ok?
public class MyPoco
{
private string _uriMapper;
public Uri SomeUri
{
get { return new Uri(_uriMapper); }
}
public string Mapper { set { _uriMapper = value; } }
}
Edit:
public class UriContainer
{
private string _uriMapper;
public string UriMapper { set { _uriMapper = value; } }
public int Id { get; set; }
public Uri SomeUri { get {return new Uri(_uriMapper);} }
}
public class DbTests
{
[Test]
public void Can_Get_A_Uri()
{
using (var c = new SqlConnection("hello"))
{
c.Open();
var uri = c.Query<UriContainer>("select *, someuri as urimapper from uris where id = 3").Single();
Console.WriteLine(uri.SomeUri);
}
}
}
I'm trying to consume a RESTful JSON web service using WCF on the client side. The service is 3rd party, so I cannot make any changes to the server response.
The server is sending back a response that looks something like this when there's only one data point...
Single Data Point
{
"Data":
{
"MyPropertyA":"Value1",
"MyPropertyB":"Value2"
},
}
and something like this when there's more than one data point...
Multiple Data Points
{
"Data":
[
{
"MyPropertyA":"Value1",
"MyPropertyB":"Value2"
},
{
"MyPropertyA":"Value3",
"MyPropertyB":"Value4"
},
{
"MyPropertyA":"Value5",
"MyPropertyB":"Value6"
}
],
}
I have my service contract set up like this...
[ServiceContract]
public interface IRewardStreamService
{
[OperationContract]
[WebInvoke]
MyResponse GetMyStuff();
}
and a data point's data contract like this...
[DataContract]
public class MyData
{
[DataMember]
public string MyPropertyA { get; set; }
[DataMember]
public string MyPropertyB { get; set; }
}
and the only way I can get the single data point response to work is if I have a single instance property like this, but this does not parse the multiple data point response...
Response for Single Instance
[DataContract]
public class MyResponse
{
[DataMember]
public MyData Data { get; set; }
}
and the only way I can get the multiple data point response to work is if I have an array / list instance property like this, but this does not parse the single data point response...
Response for Multiple Instance
[DataContract]
public class MyResponse
{
[DataMember]
public IList<MyData> Data { get; set; }
}
I understand the issue is that the response is omitting the brackets when there's only one data point returned, but it seems that WCF doesn't play well with deserializing that syntax. Is there some way I can tell the DataContractJsonSerializer to allow single element arrays to not include brackets and then tell my service to use that serializer? Maybe a service behavior or something?
Any direction would be helpful.
You can use a custom message formatter to change the deserialization of the JSON into the data contract you want. In the code below, the data contract is defined to have a List<MyData>; if the response contains only one data point, it will "wrap" that into an array prior to passing to the deserializer, so it will work for all cases.
Notice that I used the JSON.NET library to do the JSON modification, but that's not a requirement (it just has a nice JSON DOM to work with the JSON document).
public class StackOverflow_12825062
{
[ServiceContract]
public class Service
{
[WebGet]
public Stream GetData(bool singleDataPoint)
{
string result;
if (singleDataPoint)
{
result = #"{
""Data"":
{
""MyPropertyA"":""Value1"",
""MyPropertyB"":""Value2""
},
}";
}
else
{
result = #"{
""Data"":
[
{
""MyPropertyA"":""Value1"",
""MyPropertyB"":""Value2""
},
{
""MyPropertyA"":""Value3"",
""MyPropertyB"":""Value4""
},
{
""MyPropertyA"":""Value5"",
""MyPropertyB"":""Value6""
}
],
} ";
}
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
return new MemoryStream(Encoding.UTF8.GetBytes(result));
}
}
[DataContract]
public class MyData
{
[DataMember]
public string MyPropertyA { get; set; }
[DataMember]
public string MyPropertyB { get; set; }
}
[DataContract]
public class MyResponse
{
[DataMember]
public List<MyData> Data { get; set; }
public override string ToString()
{
return string.Format("MyResponse, Data.Length={0}", Data.Count);
}
}
[ServiceContract]
public interface ITest
{
[WebGet]
MyResponse GetData(bool singleDataPoint);
}
public class MyResponseSingleOrMultipleClientReplyFormatter : IClientMessageFormatter
{
IClientMessageFormatter original;
public MyResponseSingleOrMultipleClientReplyFormatter(IClientMessageFormatter original)
{
this.original = original;
}
public object DeserializeReply(Message message, object[] parameters)
{
WebBodyFormatMessageProperty messageFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name];
if (messageFormat.Format == WebContentFormat.Json)
{
MemoryStream ms = new MemoryStream();
XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(ms);
message.WriteMessage(jsonWriter);
jsonWriter.Flush();
string json = Encoding.UTF8.GetString(ms.ToArray());
JObject root = JObject.Parse(json);
JToken data = root["Data"];
if (data != null)
{
if (data.Type == JTokenType.Object)
{
// single case, let's wrap it in an array
root["Data"] = new JArray(data);
}
}
// Now we need to recreate the message
ms = new MemoryStream(Encoding.UTF8.GetBytes(root.ToString(Newtonsoft.Json.Formatting.None)));
XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max);
Message newMessage = Message.CreateMessage(MessageVersion.None, null, jsonReader);
newMessage.Headers.CopyHeadersFrom(message);
newMessage.Properties.CopyProperties(message.Properties);
message = newMessage;
}
return this.original.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
throw new NotSupportedException("This formatter only supports deserializing reply messages");
}
}
public class MyWebHttpBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
IClientMessageFormatter result = base.GetReplyClientFormatter(operationDescription, endpoint);
if (operationDescription.Messages[1].Body.ReturnValue.Type == typeof(MyResponse))
{
return new MyResponseSingleOrMultipleClientReplyFormatter(result);
}
else
{
return result;
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WebHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior());
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetData(false));
Console.WriteLine(proxy.GetData(true));
Console.Write("Press ENTER to close the host");
((IClientChannel)proxy).Close();
factory.Close();
Console.ReadLine();
host.Close();
}
}
I don't know about using WCF so I'll change to Asp.Net WCF. Here is an article that will get you one the way
http://www.west-wind.com/weblog/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing
I just can't figure out how to determine if it's an array or a single object. Here is a little code.
[TestMethod]
public void SingleObject()
{
using (var client = new HttpClient())
{
var result = client.GetStringAsync("http://localhost:8080/api/JSONTestOne");
string content = result.Result;
JObject jsonVal = JObject.Parse(content);
dynamic aFooObj = jsonVal;
Console.WriteLine(aFooObj.afoo.A);
}
}
[TestMethod]
public void ArrayWithObject()
{
using (var client = new HttpClient())
{
var result = client.GetStringAsync("http://localhost:8080/api/JSONTest");
string content = result.Result;
JObject jsonVal = JObject.Parse(content);
dynamic foos = jsonVal;
Console.WriteLine(foos[0].A);
}
}
In Python I can consume a web service so easily:
from suds.client import Client
client = Client('http://www.example.org/MyService/wsdl/myservice.wsdl') #create client
result = client.service.myWSMethod("Bubi", 15) #invoke method
print result #print the result returned by the WS method
I'd like to reach such a simple usage with Java.
With Axis or CXF you have to create a web service client, i.e. a package which reproduces all web service methods so that we can invoke them as if they where normal methods. Let's call it proxy classes; usually they are generated by wsdl2java tool.
Useful and user-friendly. But any time I add/modify a web service method and I want to use it in a client program I need to regenerate proxy classes.
So I found CXF DynamicClientFactory, this technique avoids the use of proxy classes:
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;
//...
//create client
DynamicClientFactory dcf = DynamicClientFactory.newInstance();
Client client = dcf.createClient("http://www.example.org/MyService/wsdl/myservice.wsdl");
//invoke method
Object[] res = client.invoke("myWSMethod", "Bubi");
//print the result
System.out.println("Response:\n" + res[0]);
But unfortunately it creates and compiles proxy classes runtime, hence requires JDK on the production machine. I have to avoid this, or at least I can't rely on it.
My question:
Is there another way to dinamically invoke any method of a web service in Java, without having a JDK at runtime and without generating "static" proxy classes? Maybe with a different library? Thanks!
I know this is a really old question but if you are still interested you could use soap-ws github project: https://github.com/reficio/soap-ws
Here you have a sample usage really simple:
Wsdl wsdl = Wsdl.parse("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL");
SoapBuilder builder = wsdl.binding()
.localPart("CurrencyConvertorSoap")
.find();
SoapOperation operation = builder.operation()
.soapAction("http://www.webserviceX.NET/ConversionRate")
.find();
Request request = builder.buildInputMessage(operation)
SoapClient client = SoapClient.builder()
.endpointUrl("http://www.webservicex.net/CurrencyConvertor.asmx")
.build();
String response = client.post(request);
As you can see it is really simple.
With CXF 3.x this could be possible with StaxDataBinding. Follow below steps to get the basics. Of course, this could be enhanced to your needs.
Create StaxDataBinding something like below. Note below code can be enhanced to your sophistication.
class StaxDataBinding extends AbstractInterceptorProvidingDataBinding {
private XMLStreamDataReader xsrReader;
private XMLStreamDataWriter xswWriter;
public StaxDataBinding() {
super();
this.xsrReader = new XMLStreamDataReader();
this.xswWriter = new XMLStreamDataWriter();
inInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
inFaultInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
inInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
inFaultInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
}
static class RemoveStaxInEndingInterceptor
extends AbstractPhaseInterceptor<Message> {
static final RemoveStaxInEndingInterceptor INSTANCE = new RemoveStaxInEndingInterceptor();
public RemoveStaxInEndingInterceptor() {
super(Phase.PRE_INVOKE);
addBefore(StaxInEndingInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
message.getInterceptorChain().remove(StaxInEndingInterceptor.INSTANCE);
}
}
public void initialize(Service service) {
for (ServiceInfo serviceInfo : service.getServiceInfos()) {
SchemaCollection schemaCollection = serviceInfo.getXmlSchemaCollection();
if (schemaCollection.getXmlSchemas().length > 1) {
// Schemas are already populated.
continue;
}
new ServiceModelVisitor(serviceInfo) {
public void begin(MessagePartInfo part) {
if (part.getTypeQName() != null
|| part.getElementQName() != null) {
return;
}
part.setTypeQName(Constants.XSD_ANYTYPE);
}
}.walk();
}
}
#SuppressWarnings("unchecked")
public <T> DataReader<T> createReader(Class<T> cls) {
if (cls == XMLStreamReader.class) {
return (DataReader<T>) xsrReader;
}
else {
throw new UnsupportedOperationException(
"The type " + cls.getName() + " is not supported.");
}
}
public Class<?>[] getSupportedReaderFormats() {
return new Class[] { XMLStreamReader.class };
}
#SuppressWarnings("unchecked")
public <T> DataWriter<T> createWriter(Class<T> cls) {
if (cls == XMLStreamWriter.class) {
return (DataWriter<T>) xswWriter;
}
else {
throw new UnsupportedOperationException(
"The type " + cls.getName() + " is not supported.");
}
}
public Class<?>[] getSupportedWriterFormats() {
return new Class[] { XMLStreamWriter.class, Node.class };
}
public static class XMLStreamDataReader implements DataReader<XMLStreamReader> {
public Object read(MessagePartInfo part, XMLStreamReader input) {
return read(null, input, part.getTypeClass());
}
public Object read(QName name, XMLStreamReader input, Class<?> type) {
return input;
}
public Object read(XMLStreamReader reader) {
return reader;
}
public void setSchema(Schema s) {
}
public void setAttachments(Collection<Attachment> attachments) {
}
public void setProperty(String prop, Object value) {
}
}
public static class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> {
private static final Logger LOG = LogUtils
.getL7dLogger(XMLStreamDataWriter.class);
public void write(Object obj, MessagePartInfo part, XMLStreamWriter writer) {
try {
if (!doWrite(obj, writer)) {
// WRITE YOUR LOGIC HOW you WANT TO HANDLE THE INPUT DATA
//BELOW CODE JUST CALLS toString() METHOD
if (part.isElement()) {
QName element = part.getElementQName();
writer.writeStartElement(element.getNamespaceURI(),
element.getLocalPart());
if (obj != null) {
writer.writeCharacters(obj.toString());
}
writer.writeEndElement();
}
}
}
catch (XMLStreamException e) {
throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
}
}
public void write(Object obj, XMLStreamWriter writer) {
try {
if (!doWrite(obj, writer)) {
throw new UnsupportedOperationException("Data types of "
+ obj.getClass() + " are not supported.");
}
}
catch (XMLStreamException e) {
throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
}
}
private boolean doWrite(Object obj, XMLStreamWriter writer)
throws XMLStreamException {
if (obj instanceof XMLStreamReader) {
XMLStreamReader xmlStreamReader = (XMLStreamReader) obj;
StaxUtils.copy(xmlStreamReader, writer);
xmlStreamReader.close();
return true;
}
else if (obj instanceof XMLStreamWriterCallback) {
((XMLStreamWriterCallback) obj).write(writer);
return true;
}
return false;
}
public void setSchema(Schema s) {
}
public void setAttachments(Collection<Attachment> attachments) {
}
public void setProperty(String key, Object value) {
}
}
}
Prepare your input to match the expected input, something like below
private Object[] prepareInput(BindingOperationInfo operInfo, String[] paramNames,
String[] paramValues) {
List<Object> inputs = new ArrayList<Object>();
List<MessagePartInfo> parts = operInfo.getInput().getMessageParts();
if (parts != null && parts.size() > 0) {
for (MessagePartInfo partInfo : parts) {
QName element = partInfo.getElementQName();
String localPart = element.getLocalPart();
// whatever your input data you need to match data value for given element
// below code assumes names are paramNames variable and value in paramValues
for (int i = 0; i < paramNames.length; i++) {
if (paramNames[i].equals(localPart)) {
inputs.add(findParamValue(paramNames, paramValues, localPart));
}
}
}
}
return inputs.toArray();
}
Now set the proper data binding and pass the data
Bus bus = CXFBusFactory.getThreadDefaultBus();
WSDLServiceFactory sf = new WSDLServiceFactory(bus, wsdl);
sf.setAllowElementRefs(false);
Service svc = sf.create();
Client client = new ClientImpl(bus, svc, null,
SimpleEndpointImplFactory.getSingleton());
StaxDataBinding databinding = new StaxDataBinding();
svc.setDataBinding(databinding);
bus.getFeatures().add(new StaxDataBindingFeature());
BindingOperationInfo operInfo = ...//find the operation you need (see below)
Object[] inputs = prepareInput(operInfo, paramNames, paramValues);
client.invoke("operationname", inputs);
If needed you can match operation name something like below
private BindingOperationInfo findBindingOperation(Service service,
String operationName) {
for (ServiceInfo serviceInfo : service.getServiceInfos()) {
Collection<BindingInfo> bindingInfos = serviceInfo.getBindings();
for (BindingInfo bindingInfo : bindingInfos) {
Collection<BindingOperationInfo> operInfos = bindingInfo.getOperations();
for (BindingOperationInfo operInfo : operInfos) {
if (operInfo.getName().getLocalPart().equals(operationName)) {
if (operInfo.isUnwrappedCapable()) {
return operInfo.getUnwrappedOperation();
}
return operInfo;
}
}
}
}
return null;
}
Following is part of service layer which is provided by WCF service :
[Serializable]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
public class Service1 : IService1
{
public List<WaitInfo> GetWaitingList()
{
MyDBDataContext db = new MyDBDataContext();
var query = from w in db.WAIT_INFOs
select new WaitInfo
{
TagNo = w.PATIENT_INFO.TAG_NO,
RoomName= w.ROOM_INFO.ROOM_NAME,
PName= w.PATIENT_INFO.P_NAME
};
List<WaitInfo> result = query.ToList();
return result;
}
And following is codebehind part of UI layer which is provided by Silverlight
public MainPage()
{
InitializeComponent();
Service1Client s = new Service1Client();
s.GetWaitingListCompleted +=
new EventHandler<GetWaitingListByCompletedEventArgs>( s_GetWaitingListCompleted);
s.GetWaitingListAsync();
}
void s_GetWaitingListCompleted(object sender,
RadControlsSilverlightApplication1.ServiceReference2.GetWaitingListByCompletedEventArgs e)
{
GridDataGrid.ItemsSource = e.Result;
}
And following is xaml code in Silverlight page
<Grid x:Name="LayoutRoot">
<data:DataGrid x:Name="GridDataGrid"></data:DataGrid>
</Grid>
It is very simple code, however what I am thinking weird is property name of object at "e.Result" in the code behind page.
In the service layer, although properties' names are surely "RoomName, PName, TagNo", in the silverlight properties' names are "roomName, pName, tagNo" which are private variable name of the WaitingList Object.
Did I something wrong?
Thanks in advance.
Unless you specifically decorate your class with the DataContract attribute (which you should, instead of Serializable) then a default DataContract will be inferred. For normal Serializable types, this means the fields will be serialized as opposed to the properties.
You can markup your class in either of the following two ways. The latter will use the property accessors when serializing/deserializing your object which may be very useful or be a hassle depending on your circumstances.
[DataContract]
public class WaitInfo
{
[DataMember(Name="RoomName")]
private string roomName;
[DataMember(Name="PName")]
private string pName;
[DataMember(Name="TagNo")]
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
The method I prefer:
[DataContract]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
[DataMember]
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
[DataMember]
public string PName
{ get { return pName; } set { this.pName = value; } }
[DataMember]
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}