What I want to achieve is to send to PHP complex object to PHP, currently using wsd-data but only sends the properties values from the root, ie:
public class Post : Collection<Post>
{
public string Title { get; set; }
public string Content { get; set; }
public File Image { get; set; }
}
byte[] fileContents = ....;
Post post = new Post();
post.Title = "Post title";
post.Content = "Post content";
post.Image = new File ("FileName.png", "image/png", fileContents);
await post.Save();
In this case works perfectly because it handles the File case internally, but if I add a nested dependency like
public class Post : Collection<Post>
{
public string Title { get; set; }
public string Content { get; set; }
public File Image { get; set; }
public Author Author { get; set; }
}
Lets say that Author is a class that have name, id, etc, but when I post it only sends the Author.toString() value, I tried to add an array like key to post to PHP like:
MultipartFormDataContent form = new MultipartFormDataContent ();
form.Add (new StringContent (post.Author.Name), "Author[Name]");
form.Add (new StringContent (post.Author.Id), "Author[Id]");
await httpClient.PostAsync (url, form).ConfigureAwait (false);
Then in PHP I want to receive something like this:
<?php
echo $_POST['Author']['Name']; // must print the author name
?>
But I just got an empty $_POST['Author'] variable, dunno how to achieve with c#, if I need to change internally how to create the form body just let me know, but would like to use form-data because it support file submission.
Regards
Sounds like using serialization would be a better option than trying to process your input as form data. I haven't done much PHP in along time, but I've done plenty of web services. Just serialize it on the Xamarin end, and deserialize it on the PHP end.
I've found a solution (was a flaw on the library) its here
Basically I map recursively the complex dictionary to a much simple one (one level with values). i.e.
// Pseudo class with data
Post {
Id=0,
Title="Hello world",
List<Comment> Comments=[
Comment { Id=0, Description="This is a comment" }
]
}
// Pseudo dictionary result
Dictionary<string, string> data = {
Id=0,
Title="Hello World",
Comments[0][Id]=0,
Comments[0][Description]="This is a comment"
}
// And then in php get (all keys are converted to lowercase
echo $_POST['id']; // 0
echo $_POST['title']; // Hello World
echo $_POST['comments'][0]['id']; // 0
echo $_POST['comments'][0]['description']; // This is a comment
Related
I asked a question a couple of days ago to collect data from MongoDB as a tree.
MongoDB create an array within an array
I am a newbie to MongoDB, but have used JSON quite substantially. I thought using a MongoDB to store my JSON would be a great benefit, but I am just experiencing immense frustration.
I am using .NET 4.5.2
I have tried a number of ways to return the output from my aggregate query to my page.
public JsonResult GetFolders()
{
IMongoCollection<BsonDocument> collection = database.GetCollection<BsonDocument>("DataStore");
PipelineDefinition<BsonDocument, BsonDocument> treeDocs = new BsonDocument[]
{
// my query, which is a series of new BsonDocument
}
var documentGroup = collection.Aggregate(treeDocs).ToList();
// Here, I have tried to add it to a JsonResult Data,
// as both documentGroup alone and documentGroup.ToJson()
// Also, loop through and add it to a List and return as a JsonResult
// Also, attempted to serialise, and even change the JsonWriterSettings.
}
When I look in the Immediate Window at documentGroup, it looks exactly like Json, but when I send to browser, it is an escaped string, with \" surrounding all my keys and values.
I have attempted to create a model...
public class FolderTree
{
public string id { get; set; }
public string text { get; set; }
public List<FolderTree> children { get; set; }
}
then loop through the documentGroup
foreach(var docItem in documentGroup)
{
myDocs.Add(BsonSerializer.Deserialize<FolderTree>(docItem));
}
but Bson complains that it cannot convert int to string. (I have to have text and id as a string, as some of the items are strings)
How do I get my MongoDB data output as Json, and delivered to my browser as Json?
Thanks for your assistance.
========= EDIT ===========
I have attempted to follow this answer as suggested by Yong Shun below, https://stackoverflow.com/a/43220477/4541217 but this failed.
I had issues, that the "id" was not all the way through the tree, so I changed the folder tree to be...
public class FolderTree
{
//[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
//public string id { get; set; }
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
public class FolderTreeChildren
{
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
Now, when I look at documentGroup, I see...
[0]: {Plugins.Models.FolderTree}
[1]: {Plugins.Models.FolderTree}
To be fair to sbc in the comments, I have made so many changes to get this to work, that I can't remember the code I had that generated it.
Because I could not send direct, my json result was handled as...
JsonResult json = new JsonResult();
json.Data = documentGroup;
//json.Data = JsonConvert.SerializeObject(documentGroup);
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return json;
Note, that I also tried to send it as...
json.Data = documentGroup.ToJson();
json.Data = documentGroup.ToList();
json.Data = documentGroup.ToString();
all with varying failures.
If I leave as documentGroup, I get {Current: null, WasFirstBatchEmpty: false, PostBatchResumeToken: null}
If I do .ToJson(), I get "{ \"_t\" : \"AsyncCursor`1\" }"
If I do .ToList(), I get what looks like Json in json.Data, but get an error of Unable to cast object of type 'MongoDB.Bson.BsonInt32' to type 'MongoDB.Bson.BsonBoolean'.
If I do .ToString(), I get "MongoDB.Driver.Core.Operations.AsyncCursor`1[MongoDB.Bson.BsonDocument]"
=========== EDIT 2 =================
As this way of extracting the data from MongoDB doesn't want to work, how else can I make it work?
I am using C# MVC4. (.NET 4.5.2)
I need to deliver json to the browser, hence why I am using a JsonResult return type.
I need to use an aggregate to collect from MongoDB in the format I need it.
My Newtonsoft.Json version is 11.0.2
My MongoDB.Driver is version 2.11.1
My method is the simplest it can be.
What am I missing?
I have at times made it my life's work to avoid API's at all costs (that's a debate for another day) but the time is arriving for this to change, a few months ago I got started on creating an API for my applications, and thanks to this very website, it's worked a charm.
So now I'm creating a simple Windows Console Application, it should do nothing more than get API Data then submit to a database for the primary application to use at a later date.
So far so good or so I thought.
This is what I came up with:
static void Main(string[] args)
{
string pair = "xxx/xxx";
string apiUrl = "http://someURL" + pair;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader readerStream = new StreamReader(responseStream, System.Text.Encoding.GetEncoding("utf-8"));
string json = readerStream.ReadToEnd();
readerStream.Close();
var jo = JObject.Parse(json);
Console.WriteLine("Pair : " + (string)jo["pair"]);
Console.WriteLine("Open : " + (string)jo["openPrice"]);
Console.WriteLine("Close : " + (string)jo["closePrice"]);
Console.WriteLine("Vol : " + (string)jo["vol"]);
Console.ReadKey();
}
Now this works FANTASTIC for the primary source, but when I change the source (eventually it will be multi source) it fails to work.
After some investigation, it seems the slightly different responses are the culprit.
API Return looks like this
{
"ip":"1.1.1.1","country_code":"AU","country_name":"Australia",
"region_code":"VIC","region_name":"Victoria","city":"Research",
"zip_code":"3095","time_zone":"Australia/Melbourne","latitude":-37.7,
"longitude":145.1833,"metro_code":0
}
The return for an alternative source looks like this
{
"success":true,"message":"",
"result":[
{"MarketName":"BITCNY-BTC","High":8000.00000001,"Low":7000.00000000,
"Volume":0.02672075, "Last":7000.00000000,"BaseVolume":213.34995000,
"TimeStamp":"2017-02-09T08:38:22.62","Bid":7000.00000001,"Ask":9999.99999999,
"OpenBuyOrders":14,
"OpenSellOrders":20,"PrevDay":8000.00000001,"Created":"2015-12-11T06:31:40.653"
}
]
}
As we can see the second return is structured differently, and for this feed I've been unable to work this out, clearly my code works, kind of, and clearly it doesn't.
I've looked about on the net and am still no closer to a solution, partly I don't know what I'm really asking and secondly google only wants to talk webAPI.
If there is someone who can point me in the right direction, I don't want the work done for me per say that solves nothing, I have got to learn to do this one way or another.
As mentioned in the comments, your strings are unrelated. You won't be able to perform the same actions on both JSON strings because they're different.
Your best bet is to work with Deserialization to objects, which is converting a JSON string into an object and working with properties instead of JObject literals.
Putting your first JSON string into Json2Csharp.com, this class structure was produced:
public class RootObject
{
public string ip { get; set; }
public string country_code { get; set; }
public string country_name { get; set; }
public string region_code { get; set; }
public string region_name { get; set; }
public string city { get; set; }
public string zip_code { get; set; }
public string time_zone { get; set; }
public double latitude { get; set; }
public double longitude { get; set; }
public int metro_code { get; set; }
}
You could then make some adjustments to your code to extract the HTTP request functionality, assuming that you'll only be working with JSON strings in this instance, and then simply deserialized the JSON to an object.
An example of a WebRequest() method would be:
private static string WebRequest(string UrlToQuery)
{
using(WebClient client = new WebClient)
{
return client.DownloadString(UrlToQuery);
}
}
(Make sure you catch any errors and deal with unexpected responses, I'll leave that up to you!)
You can then call this method like so:
string JsonResponse = WebRequest(apiUrl);
and deserialize to an object of type RootObject like so:
RootObject deserializedString = JsonConvert.Deserialize<RootObject>(JsonResponse);
You'll then be able to perform something like:
Console.WriteLine($"Country name: {deserializedString.country_name}");
Which will print the value of the country_name property of your deserializedString object.
I've created a successful connection to ES, and then written my json query. Now, I would like to send that query via the Serialize method.
The Serialize method requires two parameters:
1. object and
2. Stream writableStream
My question is, with the second one. When I create a stream with the following code line:
Stream wstream;
And use it to initialize my json2 variable with the following code:
var json2 = highLevelclient.Serializer.Serialize(query, wstream).Utf8String();
I get the following error on the wstream variable:
Use of unassigned local variable 'wstream'.
Am I missing something? Is it the way I create the wstream variable that is wrong? thank you.
/* \\\ edit: ///// */
There is another issue now, I use Searchblox to index and search my files, which itself calls ES 2.x to do the job. Searchblox uses a "mapping.json" file to initialize a mapping upon the creation of an index. Here's the link to that file.
As "#Russ Cam" suggested, I created my own class content with the following code (just like he did with the "questions" index and "Question" class):
public class Content
{
public string type { get; set; }
public Fields fields { get; set; }
}
public class Fields
{
public Content1 content { get; set; }
public Autocomplete autocomplete { get; set; }
}
public class Content1
{
public string type { get; set; }
public string store { get; set; }
public string index { get; set; }
public string analyzer { get; set; }
public string include_in_all { get; set; }
public string boost { get; set; }
} //got this with paste special->json class
These fields from the content class (type,store etc.) come from the mapping.json file attached above.
Now, when I (just like you showed me) execute the following code:
var searchResponse = highLevelclient.Search<Content>(s => s.Query(q => q
.Match(m => m.Field(f => f.fields.content)
.Query("service")
All I get as a response on the searchResponse variable is:
Valid NEST response built from a successful low level call on POST: /idx014/content/_search
Audit trail of this API call:
-HealthyResponse: Node: http://localhost:9200/ Took: 00:00:00.7180404
Request:
{"query":{"match":{"fields.content":{"query":"service"}}}}
Response:
{"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
And no documents in searchResponse.Documents.
Contradictorily, when I search for the "service" query on Searchblox or make an API call to localhost:9200 with the Sense extension of Google Chrome, I get 2 documents. (the documents that I was looking for)
In brief, all I want is to be able to :
get all the documents (no criteria)
get all the documents within a time range and based upon keywords.. such as "service"
What am I doing wrong? I can provide with more information if needed..
Thank you all for your detailed answers.
It's actually much simpler than this with NEST :) The client will serialize your requests for you and send them to Elasticsearch, you don't need to take the step to serialize them yourself and then pass them to the client to send to Elasticsearch.
Take a search request for example. Given the following POCO
public class Question
{
public string Body { get; set; }
}
We can search for questions that contain the phrase "this should never happen" in the body with
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.InferMappingFor<Question>(m => m
.IndexName("questions")
);
var client = new ElasticClient(settings);
var searchResponse = client.Search<Question>(s => s
.Query(q => q
.MatchPhrase(m => m
.Field(f => f.Body)
.Query("this should never happen")
)
)
);
// do something with the response
foreach (var question in searchResponse.Documents)
{
Console.WriteLine(question.Body);
}
this line
My question is, with the second one. When I create a stream with the following code line:
Stream wstream;
does not create na object. It nearly declares it. You need to new-ed it.
Stream wstream = new MemoryStream(); //doesn't have to be MemoryStream here - check what does Serialize expects
Just remember to close it later or use using statement.
using(Stream stream = new MemoryStream())
{
//do operations on wstream
} //closes automatically here
You just declared wstream but never assigned an actual stream to it. Depending on how Serialize method works it could be:
you need to create a stream and pass it to the Serialize method
or you need to pass stream parameter with out prefix
Sending a plain text notification is easy and well documented. But I've been pulling my hair today regarding sending a custom notification for iOS that has the alert and some fields like userId.
I started with this help page and implemented something similar to the last sample, then I found this answer that seems to invalidate the last sample on the help page, as the "url" property should be outside the "aps" object. I tried a good deal of combinations but each one of them gets sent as text to the app (the whole message, with the "default" property and "APNS" object)...
If I explicitly set MessageStructure to json I get the error: "Invalid parameter: Message Structure - JSON message body failed to parse" but I'm pretty sure my JSON is good, when sent to SNS the string in the Message property looks like this:
{ "default":"You received a new message from X.",
"APNS_SANDBOX":"{ \"aps\": {\"alert\":\"You received a new message from X.\"},
\"event\":\"Message\",
\"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"
}",
"APNS":"{ \"aps\": {\"alert\":\"You received a new message from X.\"},
\"event\":\"Message\",
\"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"
}"
}
Does anybody have a good example of sending a notification with custom payload through SNS in C#? Because Amazon sure hasn't...
Strangely when I implemented the clean way of doing this by using classes and serializing objects instead of just sending a formatted string it worked. The only difference was the spacing... in the clean version there are no spaces except in the property values:
{"default":"You received a new message from X.","APNS_SANDBOX":"{\"aps\":{\"alert\":\"You received a new message from X.\"},\"event\":\"Message\",\"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"}","APNS":"{\"aps\":{\"alert\":\"You received a new message from X.\"},\"event\":\"Message\",\"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"}"}
These are the classes that I'm serializing (only for APNS for the moment), use whatever properties you need instead of Event and ObjectID:
[DataContract]
public class AmazonSNSMessage
{
[DataMember(Name = "default")]
public string Default { get; set; }
[DataMember(Name = "APNS_SANDBOX")]
public string APNSSandbox { get; set; }
[DataMember(Name = "APNS")]
public string APNSLive { get; set; }
public AmazonSNSMessage(string notificationText, NotificationEvent notificationEvent, string objectID)
{
Default = notificationText;
var apnsSerialized = JsonConvert.SerializeObject(new APNS
{
APS = new APS { Alert = notificationText },
Event = Enum.GetName(typeof(NotificationEvent), notificationEvent),
ObjectID = objectID
});
APNSLive = APNSSandbox = apnsSerialized;
}
public string SerializeToJSON()
{
return JsonConvert.SerializeObject(this);
}
}
[DataContract]
public class APNS
{
[DataMember(Name = "aps")]
public APS APS { get; set; }
[DataMember(Name = "event")]
public string Event { get; set; }
[DataMember(Name = "objectID")]
public string ObjectID { get; set; }
}
[DataContract]
public class APS
{
[DataMember(Name = "alert")]
public string Alert { get; set; }
}
So I get the Amazon SNS message by doing:
new AmazonSNSMessage(...).SerializeToJSON();
The key that just fixed this for me was realizing that the outer JSON specific to SNS (e.g. the "default" and "APNS" properties) MUST NOT be escaped, ONLY the inner payload. For instance, this Message value succeeded (just posting the start):
{"APNS":"{\"aps\" ...
Notice the first property "APNS" is not escaped, but then it's value (your actual payload that will hit the device) IS escaped. The following gets the job done:
JObject payload = ... // your apns, etc payload JObject
var snsMsgJson = new JObject(
new JProperty("default", "message 1 2 3"), // "default" is optional if APNS provided
new JProperty("APNS", payload.ToString(Formatting.None))
);
string str = snsMsgJson.ToString(Formatting.None);
I figured this out looking at the example above, thanks! But, I knew the above 'clean classes' so-called solution couldn't and shouldn't actually be a requirement. So when he says: "The only difference was the spacing... in the clean version there are no spaces except in the property values" that is not correct. The real difference is as I said, outer (SNS specific) JSON shall not be escaped, but inner must.
Soap box: How about that documentation eh? So many things in this API that wasted major time and just as importantly: one's sense of well-being. I do appreciate the service though regardless.
I am trying to call a rest api method from c#. Problem is for all content types it passes null to body parameter.I shared my code below.Apart from this code I have tried to write body parameter to request as stream.It didn't work either. I have also tried 'application/x-www-form-urlencoded' as content type.
Calling rest api method from c# sample:
string token = Server.UrlEncode("v0WE/49uN1/voNwVA1Mb0MiMrMHjFunE2KgH3keKlIqei3b77BzTmsk9OIREken1hO9guP3qd4ipCBQeBO4jiQ==");
string url = "http://localhost:2323/api/Applications/StartProcess?token=" + token;
string data = #"{""ProcessParameters"": [{ ""Name"":""flowStarter"",""Value"": ""Waffles"" }],
""Process"": ""RESTAPISUB""}";
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new System.Uri(url);
byte[] cred = UTF8Encoding.UTF8.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
System.Net.Http.HttpContent content = new StringContent(data, UTF8Encoding.UTF8, "application/json");
HttpResponseMessage messge = client.PostAsync(url, content).Result;
string description = string.Empty;
if (messge.IsSuccessStatusCode)
{
string result = messge.Content.ReadAsStringAsync().Result;
description = result;
}
Rest api Method:
[HttpPost]
[ActionName("StartProcess")]
public int StartProcess([FromUri]string token,[FromBody]WorkflowStartParameters parameters)
{
try
{
LoginInformation info = CasheProcesses.ReadCashe(token);
eBAWSAPI api = Service.GetWSService();
WorkflowProcess proc = api.StartProcess(info.Id, info.Password, info.ImpersonateUserId, info.Language, parameters);
return proc.ProcessId;
}
catch (Exception ex)
{
throw new Exception("An error occured when starting process,exception detail:" + ex);
}
}
WorkflowStartParameters class structure:
public class WorkflowStartParameters
{
public WorkflowParameter[] ProcessParameters;
public string Process { get; set; }
}
public class WorkflowParameter
{
public string Name { get; set; }
public string Value { get; set; }
}
I have searched this problem a lot. It seems as a very common problem. I just found this solution working properly, passing request parameter to rest api method and reading body parameter from there. But it is not a valid solution for me.
If you have any idea,feel free to share.
Thanks,
Zehra
I don´t know if it can solve your problem, but let me try.
I guess you don´t have to utilize Server.UrlEncode in your call, but:
Dim myUri As New Uri(Token)
And I guess you must not encode also your username and password - try pass them as string.
Your problem appear to be here:
public class WorkflowStartParameters
{
public WorkflowParameter[] ProcessParameters; <---- needs get/set
public string Process { get; set; }
}
This needs to be a public property to serialize properly. Currently you have it set up as a public field. Just add { get; set; } and give that a try. I would also look into serializing with Newtonsoft.Json to ensure your object is properly serialized. Trying to do it with escape strings will be messing the more data you are sending.
By the way there can be issues sometimes serializing arrays, I would change that to :
public List<WorkflowParameter> ProcessParameters{get;set;}
Finally I have achieved to send filled out data to server. It was about serialization problem. But it didn't work with json serialization before send data. I have added DataContract attribute to my class and it works properly.
Unfortunately still I couldn't figure out this when I make ajax calls from java script it works without DataContract attribute but if I call it in c# it needs DataContract attribute. If someone share the information about this I would appreciate!
I am sharing new class structure, everything else but this still same:
[Serializable]
[DataContract]
public class WorkflowParameter
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
}
[Serializable]
[DataContract]
public class WorkflowStartParameters
{
[DataMember]
public WorkflowParameter[] ProcessParameters { get; set; }
[DataMember]
public string Process { get; set; }
}