I created a WCF service to return JSON, however, it is not returned in the way i need it.
My interface:
[ServiceContract]
public interface IService1
{
[OperationContract(Name="Sensors")]
[WebInvoke(
Method = "GET",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "test")]
List<Sensor> testdata();
}
My service:
public class Service1 : IService1
{
public List<Sensor> testdata()
{
return HamsData.GetSensorDetails("1");
}
}
HamsData.GetSensorDetails:
public static List<Sensor> GetSensorDetails(string id)
{
List<Sensor> al = new List<Sensor>();
DateTime now = DateTime.Now;
DateTime thishour = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);
using ( HAMSDataClassesDataContext db = new HAMSDataClassesDataContext())
{
var dbsensors = (from p in db.sensordetails where p.sensorlatestdate == thishour select new Sensor { name = p.sensor.name, value = p.sensorlatestvalue });
foreach (Sensor x in dbsensors)
{
al.Add(x);
}
return al;
}
}
The Sensor class:
[Serializable]
[DataContract(Name = "mysensor")]
public class Sensor
{
[DataMember]
public string name { get; set; }
[DataMember]
public decimal value { get; set; }
}
This return the following JSON:
[{"name":"EIC","value":1000.000},{"name":"GIC","value":0.000},{"name":"WIC","value":0.000},{"name":"EHC","value":0.010},{"name":"GHC","value":0.000},{"name":"WHC","value":0.000},{"name":"EDC","value":33458.560},{"name":"ENC","value":27450.040},{"name":"GRC","value":35227.100},{"name":"WRC","value":38.390},{"name":"ECR","value":1.000},{"name":"10:D8:A2:DC:01:08:00:E0:T","value":24.120},{"name":"10:94:3D:B4:01:08:00:BC:T","value":46.310},{"name":"10:31:85:70:01:08:00:4D:T","value":20.940},{"name":"10:5D:5E:B4:01:08:00:EA:T","value":7.690},{"name":"10:DD:56:B4:01:08:00:3E:T","value":17.690},{"name":"28:51:32:5D:02:00:00:0A:T","value":12.560},{"name":"26:B1:08:81:00:00:00:39:T","value":15.030},{"name":"26:B1:08:81:00:00:00:39:H","value":-29.890},{"name":"26:CD:A0:6D:00:00:00:8A:T","value":15.030},{"name":"26:CD:A0:6D:00:00:00:8A:L","value":204.600}]
However, I want it without the name and value tags like:
[{"EIC":1000.000},{"GIC":0.000},{"WIC":0.000},{"EHC":0.010},{"GHC":0.000},{"WHC":0.000},{"EDC":33458.560},{"ENC":27450.040},{"GRC":35227.100},{"WRC":38.390},{"ECR":1.000},{"10:D8:A2:DC:01:08:00:E0:T":24.120},{"10:94:3D:B4:01:08:00:BC:T":46.310},{"10:31:85:70:01:08:00:4D:T",e":20.940},{"10:5D:5E:B4:01:08:00:EA:T":7.690},{"10:DD:56:B4:01:08:00:3E:T":17.690},{"28:51:32:5D:02:00:00:0A:T"12.560},{"26:B1:08:81:00:00:00:39:T":15.030},{"26:B1:08:81:00:00:00:39:H":-29.890},{"26:CD:A0:6D:00:00:00:8A:T":15.030},{"26:CD:A0:6D:00:00:00:8A:L":204.600}]
Can someone tell what i'm doing wrong here?
Thats not json, thats the problem. Json encoding is a combination of property/value. You are trying to misuse it.
Are you really sure you want to encode the data that way? It makes it far hard to extract the values.
Related
I have to send a request object via Refit which contains 2 IEnumerable and one string, but for some reason I can't send the object forward.
I've tried to use all the paramets from the interface. Ex: [Query(CollectionFormat.Csv)] or Multi / Pipes but no success.
I've also tried to create my own CustomUrlParameterFormatter but unfortunately here I'm stuck, because I don't see a good way to retrieve the name of the property from the object request that I'm sending.
The code for CustomUrlParameterFormatter
public class CustomUrlParameterFormatter : IUrlParameterFormatter
{
public string Format(object value, ParameterInfo parameterInfo)
{
if(value is IEnumerable enumerable)
{
var result = ToQueryString(enumerable, parameterInfo.Name);
return result;
}
return string.Empty;
}
public static string ToQueryString(IEnumerable query, string parameterName)
{
var values = query.Cast<object>().Select(ToString).ToArray();
var separator = parameterName + "=";
return values.Any() ? separator + string.Join("&" + separator, values) : "";
}
public static string ToString(object value)
{
var json = JsonConvert.SerializeObject(value).Replace("\\\"", "\"").Trim('"');
return Uri.EscapeUriString(json);
}
}
The Call from the IService that I'm using
[Get("/TestMethod")]
Task<HttpResponseMessage> TestMethod([Query]TestRequestDTO requestDTO, [Header("X-Correlation-ID")] string correlationId);
The Request object
public class TestRequestDTO
{
public IEnumerable<long> EnumOne { get; set; }
public IEnumerable<long> EnumTwo { get; set; }
public string MethodString { get; set; }
}
Also the RefitClient configuration
var refitSettings = new RefitSettings();
refitSettings.UrlParameterFormatter = new CustomUrlParameterFormatter();
services.AddRefitClient<IService>(refitSettings)
.ConfigureHttpClient(c => c.BaseAddress = new Uri(settings.Services.IService));
What I'm trying to achieve is something like
TestMethod?EnumOne =123&EnumOne =321&EnumTwo=123&EnumTwo=321&methodString=asdsaa
and instead I'm receiving other behavior
without CustomUrlParameterFormatter()
TestMethod?EnumOne=System.Collections.Generic.List`1%5BSystem.Int64%5D&EnumTwo=System.Collections.Generic.List`1%5BSystem.Int64%5D&MethodString=sdf
I'm trying to use WCF to consume a 3rd-party REST service which responds with URL-encoded data:
a=1&b=2&c=3
I have this now:
[DataContract]
class Response {
[DataMember(Name="a")]
public int A { get;set;}
[DataMember(Name="b")]
public int B { get;set;}
[DataMember(Name="c")]
public int C { get;set;}
}
[ServiceContract]
interface IService
{
[OperationContract]
Response Foo();
}
But it comes back with:
There was an error checking start element of object of type Response. The data at the root level is invalid. Line 1, position 1.
WCF does not "understand" forms-data content type (application/x-www-forms-urlencoded), so it won't be able to read that response directly. You can either implement a message formatter which will be able to convert that format into your contract, or you can receive the response as a Stream (which will give you the full bytes of the response), which you can decode into your class.
The code below shows how you could implement a formatter for this operation. It's not generic, but you should get the picture of what needs to be done.
public class StackOverflow_16493746
{
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "*")]
public Stream GetData()
{
WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-www-form-urlencoded";
return new MemoryStream(Encoding.UTF8.GetBytes("a=1&b=2&c=3"));
}
}
[ServiceContract]
interface IService
{
[WebGet]
Response Foo();
}
[DataContract]
class Response
{
[DataMember(Name = "a")]
public int A { get; set; }
[DataMember(Name = "b")]
public int B { get; set; }
[DataMember(Name = "c")]
public int C { get; set; }
}
public class MyResponseFormatter : IClientMessageFormatter
{
private IClientMessageFormatter originalFormatter;
public MyResponseFormatter(IClientMessageFormatter originalFormatter)
{
this.originalFormatter = originalFormatter;
}
public object DeserializeReply(Message message, object[] parameters)
{
HttpResponseMessageProperty httpResp = (HttpResponseMessageProperty)
message.Properties[HttpResponseMessageProperty.Name];
if (httpResp.Headers[HttpResponseHeader.ContentType] == "application/x-www-form-urlencoded")
{
if (parameters.Length > 0)
{
throw new InvalidOperationException("out/ref parameters not supported in this formatter");
}
byte[] bodyBytes = message.GetReaderAtBodyContents().ReadElementContentAsBase64();
NameValueCollection pairs = HttpUtility.ParseQueryString(Encoding.UTF8.GetString(bodyBytes));
Response result = new Response();
foreach (var key in pairs.AllKeys)
{
string value = pairs[key];
switch (key)
{
case "a":
result.A = int.Parse(value);
break;
case "b":
result.B = int.Parse(value);
break;
case "c":
result.C = int.Parse(value);
break;
}
}
return result;
}
else
{
return this.originalFormatter.DeserializeReply(message, parameters);
}
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
throw new NotSupportedException("This is a reply-only formatter");
}
}
public class MyClientBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
return new MyResponseFormatter(base.GetReplyClientFormatter(operationDescription, endpoint));
}
}
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<IService> factory = new ChannelFactory<IService>(new WebHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyClientBehavior());
IService proxy = factory.CreateChannel();
Response resp = proxy.Foo();
Console.WriteLine("a={0},b={1},c={2}", resp.A, resp.B, resp.C);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
In WCF Service I am making
[ServiceContract]
public interface IService1
{
[OperationContract]
CompositeType QuestionRetrieve(String email);
}
[DataContract]
public class CompositeType
{
private String imageName;
public string ImageName
{
get { return imageName; }
set { imageName= value; }
}
In Service1.cs
public CompositeType QuestionRetrieve(String email)
{
context = new myEntities();
Profile aProfile= new Profile();
aProfile= (from c in context.Questions
where c.QuestionId == email
select c).First();
CompositeType aCompositeType = new CompositeType();
aCompositeType.imageName= aProfile.ImageName;
return aCompositeType;
}
In Other WindowForm I tried to retrieving the value but it show me WindowApplicationName, ServiceName and Class name "CompositeType"
In WindowForm we are doing on PageLoad:
Service aService = new Service();
Label1.Text = aService.QuestionRetrieve("gh#gmail.com").ToString();
It showing me
WindowFom.Service.CompositeType
Can you tell where is my error I am working with Entity.
You're executing the ToString() of the CompositeType object. Do this in stead:
Label1.Text = aService.QuestionRetrieve("gh#gmail.com").ImageName;
I'm trying to use WCF service with raw messages.
1) WCF service code:
[DataContract]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
public static List<Person> CreateEmployees()
{
List<Person> lstPersons = new List<Person>()
{
new Person { Id = 1, FirstName = "Andrey", LastName = "Andreyev" },
new Person { Id = 2, FirstName = "Sergey", LastName = "Sergeyev" }
};
return lstPersons;
}
[ServiceContract]
public interface ITestService
{
[OperationContract(Action = TestService.RequestAction, ReplyAction = TestService.ReplyAction)]
Message GetPersonById(Message id);
}
public class TestService : ITestService
{
public const String ReplyAction = "http://localhost:4249/Message_ReplyAction";
public const String RequestAction = "http://localhost:4249/Message_RequestAction";
public Message GetPersonById(Message id)
{
string firstName = Employees.CreateEmployees().First(e => e.Id == id.GetBody<int>()).FirstName;
Message response = Message.CreateMessage(id.Version, ReplyAction, firstName);
return response;
}
}
2) Client code:
static void Main(string[] args)
{
TestServiceClient client = new TestServiceClient();
String RequestAction = "http://localhost:4249/Message_RequestAction";
int value = 1;
Message request = Message.CreateMessage(MessageVersion.Default, RequestAction, value);
Message reply = client.GetPersonById(request);
string firstName = reply.GetBody<string>();
Console.WriteLine(firstName);
client.Close();
}
When I run the client with: int value = 1 everything works fine. But, when I use: int value = 2 I get the following error:
Error in line 1 position 276. Expecting element 'string' from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'.. Encountered 'Element' with name 'Fault', namespace 'http://www.w3.org/2003/05/soap-envelope'.
At line:
string firstName = reply.GetBody<string>();
The service is started and I've added the service reference through "Add Service Reference..." in VS2008. I use .NET Framework 3.5.
I'm not sure why I'm getting this error.
Thank you in advance for help.
Goran
Well, you're getting a SOAP Fault. Try logging the message (either with Fiddler, with WCF Logging, or by just reading from Message.GetReaderAtBodyContents from the message you get from the server on the client-side), and see what the fault actually says. Make sure you turn on IncludeExceptionDetailInFaults on the server side.
First the code:
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[OperationContract]
public string Expression { get; set; }
[OperationContract]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
A dispatcher encodes data as xml and sends it to an IWorker instance where each expression is evaluated. When an IWorker instance evaluates an expression as true, IAction.Execute is called and the xml/data is passed.
What's the best way to serialize Rule.Actions? I've started writing a custom serializer but I'd prefer to see if there is an easier way.
Thanks.
I dont think you can use interfaces in DataContracts (someone correct me if im wrong, but i assume thats like trying to use a generic too). What I do, is have a parent class, then add the KnownType attribute. For instance
[DataContract]
public class Action
{
//members and properties
}
[DataContract]
public class SomeOtherAction:Action
{
//more implimentation
}
[DataContract]
[KnownType(typeof(SomeOtherAction))]
public class Rule
{
[DataMember]
List<Action> Actions{get;set;}
}
Now you can stuff any object that inherits from the parent Action object in to the Actions list, and it will properly serialize all their respective class properties (as long as the object is listed as a knowntype).
*I used "Action" name as an example to relate to yours, obviously Action is a keyword in .NET
Serialization is the process of converting between an object data and bytes which can be transferred over the wire. Interfaces define behavior, so by default WCF can't serialize such data. If you have the exact same assemblies on the client and the server, however, you can use the NetDataContractSerializer, which will essentially serialize (and be able to serialize) all the type information for the objects being serialized, so it can be recreated at the other side.
The code below shows how to use the NetDataContractSerializer in a service for that (based on the main example for this, the post from Aaron Skonnard at http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx)
public class StackOverflow_6932356
{
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[DataMember]
public string Expression { get; set; }
[DataMember]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
public class Service : IWorker
{
static List<IAction> AllActions = new List<IAction>();
public void Process(XmlElement data)
{
foreach (var action in AllActions)
{
action.Execute(data);
}
}
public void Update(Rule rule)
{
AllActions = rule.Actions;
}
}
public class Action1 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
public class Action2 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractSerializerOperationBehavior(OperationDescription operationDescription)
: base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
}
static void ReplaceDCSOB(ServiceEndpoint endpoint)
{
foreach (var operation in endpoint.Contract.Operations)
{
for (int i = 0; i < operation.Behaviors.Count; i++)
{
if (operation.Behaviors[i] is DataContractSerializerOperationBehavior)
{
operation.Behaviors[i] = new NetDataContractSerializerOperationBehavior(operation);
break;
}
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IWorker), new BasicHttpBinding(), "");
ReplaceDCSOB(endpoint);
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IWorker>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ReplaceDCSOB(factory.Endpoint);
var proxy = factory.CreateChannel();
proxy.Update(new Rule
{
Expression = "Expr",
Actions = new List<IAction> { new Action1(), new Action2() }
});
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><foo>bar</foo></root>");
proxy.Process(doc.DocumentElement);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}