I am developing a news-app for Windows 8 (in C#, XAML). Unfortunately I encountered a strange error after downloading a JSON-Feed (validated with http://jsonlint.com/) asynchronously. The download succeeds and then I want to parse the result: var items = Windows.Data.JsonArray.Parse(result);.
When I run the code I get the following error:
Invalid character at position 0. and Invalid JSON string.
Json.JsonArray is a new Library from Microsoft. I also tried Newtonsoft's JSON-library with the same errors. What am I doing wrong?
This is the full code:
// Retrieve recipe data from Azure
var client = new HttpClient();
client.MaxResponseContentBufferSize = 1024*1024; // Read up to 1 MB of data
var response = await client.GetAsync(new Uri("http://contosorecipes8.blob.core.windows.net/AzureRecipes"));
var result = await response.Content.ReadAsStringAsync();
// Parse the JSON recipe data
var recipes = JsonArray.Parse(result.Substring(1, result.Length - 1));
This code snippet is from a Microsoft Hands-On Lab (Contoso CookBook). I also tried it without the "[" and "]" in the source (with no effect)...
Thank you!
I was able to download and parse the result fine using this:
static async Task<JsonValue> DownloadJsonAsync(string url)
{
var client = new HttpClient();
client.MaxResponseContentBufferSize = 1024 * 1024;
var data = await client.GetByteArrayAsync(new Uri(url));
var encoding = Encoding.UTF8;
var preamble = encoding.GetPreamble();
var content = encoding.GetString(data, preamble.Length, data.Length - preamble.Length);
var result = JsonValue.Parse(content);
return result;
}
The BOM in the response wasn't handled correctly apparently which resulted in having a '\xfeff' character in the beginning killing the parser. Stripping off the preamble and parsing reads fine. Otherwise parsing it as-is throws a FormatException with the message: Encountered unexpected character 'ï'..
I was able to run your code after a small modification. The byte order mark of the UTF8 string seems to triggers a problem with JsonArray.Parse() from Windows.Data.Json.
A way to solve it without using additional encoding is to replace the BOM character after ReadAsStringAsync(), e.g.
result = result.Replace('\xfeff', ' ');
or better
if (result.Length > 1 && result[0] == '\xfeff'){
result = result.Remove(0, 1);
}
Related
I'm trying to upload a file using Microsoft's Graph SDK but have hit a problem.
I have pretty much copied verbatim the C# example from here, commented-out the progress part and updated the using statement for C# 8, and here's what I have...
public async Task<bool> UploadFileAsync(string parentFolderId, string filename, byte[] bytes)
{
var graphClient = GetGraphClient();
// Declare the variable outside the `using` statement to get around a little C# problem - https://www.tabsoverspaces.com/233779-using-await-using-iasyncdisposable-with-configureawait
var memoryStream = new MemoryStream(bytes);
await using (memoryStream.ConfigureAwait(false))
{
// Use properties to specify the conflict behavior.
// - in this case, replace.
var uploadProps = new DriveItemUploadableProperties
{
AdditionalData = new Dictionary<string, object> {{"#microsoft.graph.conflictBehavior", "replace"}},
ODataType = null
};
try
{
// Create the upload session.
// - itemPath does not need to be a path to an existing item.
var uploadSession = await graphClient.Drives[_driveId]
.Items[parentFolderId]
.ItemWithPath(filename)
.CreateUploadSession(uploadProps)
.Request()
.PostAsync()
.ConfigureAwait(false);
// Max slice size must be a multiple of 320KB.
const int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, memoryStream, maxSliceSize);
// Upload the file.
var uploadResult = await fileUploadTask.UploadAsync().ConfigureAwait(false);
if (uploadResult.UploadSucceeded)
{
// The ItemResponse object in the result represents the created item.
return true;
}
return false;
}
catch (ServiceException exception)
{
// ...
}
}
}
However the line...
var uploadSession = await graphClient.Drives[_driveId]
.Items[parentFolderId]
...
...throws an exception:
Microsoft.Graph.ServiceException: Code: BadRequest
Message: Multiple action overloads were found with the same binding parameter for 'microsoft.graph.createUploadSession'.
Can anyone help?
I figured out the cause of the problem - the filename that I was using contained invalid symbols (in my case I was stringifying a DateTime and that contained :).
It's frustrating that this exception doesn't bubble-up correctly and instead I got that "Multiple action overloads" message.
I am using BotFramework to draw strings on a PNG image. Then I convert it to a base64 string from a byte array in memory. I post the base64 string to a service and get the correct response. Everything works fine but, I get the "Sorry, my bot code is having an issue" message after the process.
case 5:
{
try
{
...
graphics.DrawString(text, fonti, brush, drawRect, stringFormat);
using (MemoryStream m = new MemoryStream())
{
image.Save(m, image.RawFormat);
// I've tried changing this line to String or StringBuilder but
// nothing changed
IMAGE = $"data:image/png;base64,{Convert.ToBase64String(m.ToArray())}";
m.Close();
}
}
catch { await context.PostAsync("ERR1"); }
string json = null;
try
{
string FormStuff = string.Format($"somecontent");
StringContent content = new StringContent(
FormStuff,
Encoding.UTF8,
"application/x-www-form-urlencoded");
string url = string.Format("http://www.example.com/");
var response = await client.PostAsync(url, content);
json = (await response.Content.ReadAsStringAsync()).ToString();
}
catch { await context.PostAsync("ERR2"); }
...
}
break;
IMAGE variable is a string.
Whenever I remove or change Convert.ToBase64String() the problem is gone but then I can't use the service as I want and the process is broken.
Only problem here is the exception thrown and shown to the end user.
Sorry, my bot code is having an issue
EDIT: I found out that if the content in my post request is too long, I get the error message. I've tried using FormUrlEncodedContent but it throws this:
Invalid URI: The Uri string is too long.
How could I post it in another way?
I do not know what service you are calling or do not have all the values for your parameters to test this with. but this is too long for a comment.
Try this instead of .ToArray() try something like
var image = Convert.ToBase64String(File.ReadAllBytes("FileName"));
You also may want to try this setting this to a variable outside of the string you are forming IMAGE with. Something like this I have had success with in the past, but again I do not know what your service is doing or expecting.
var image = Convert.ToBase64String(File.ReadAllBytes(
"FileName"));
image64 = "data:image/png;base64," + image;
I have the following code to deserilize JSON objects sent over a stream.
When running this code with network stream, I get an error message every time the jsonTextReader.LinePosition crosses the value 1000. Even if it's the same object that has been previously successfully deserialized. i.e. all the JSON messages being sent are correctly formatted and match the Schema.
And when using a MemoryStream, weirdly, this code works perfectly irrespective of the jsonTextReader.LinePosition.
Has anyone else experienced similar issue when the LinePosition goes beyond 1000?
var Serializer = new JsonSerializer()
{
NullValueHandling = NullValueHandling.Ignore
};
var streamReader = new StreamReader(stream, new UTF8Encoding());
var jsonTextReader = new JsonTextReader(streamReader)
{
CloseInput = false,
SupportMultipleContent = true
};
while (true)
{
if (jsonTextReader.Read())
{
message = Serializer.Deserialize<MyObject>(jsonTextReader);
}
}
PS I can't move the if (jsonTextReader.Read()) to the while loop because it's done in a library function. In short, I can't change the flow of code.
Update
#Tewr suggested that it could be maxlength some value restricted to 1000/1024 on the source stream. But when I run the following, it works fine.
while (true)
{
if (jsonTextReader.Read())
{
message = Serializer.Deserialize<MyObject>(jsonTextReader);
}
else
{
jsonTextReader = new JsonTextReader(streamReader);
}
}
But the problem is, I can't do this because as mentioned, part of the code is run in a library. And this doesn't entirely resolve the problem (what if there is a single JSON object string longer than 1024?).
Now, when creating a new JsonTextReader, everything is reset. Including jsonTextReader.LinePosition. So, not letting jsonTextReader.LinePosition hitting 1000 makes things work. In that case, what is the relation between jsonTextReader.LinePosition and NetworkStream/Socket?
Here are some of the error message I get:
Unexpected character encountered while parsing number: T. Path 'timeStamp', line 1, position 1026.
for
{"version":"35","timestamp":"2018-05-14T07:52:23.3347793Z","message":{"type":"msgType","value":{"id":"44","params":["AA","AC","BD"]}}}
and
Invalid JavaScript property identifier character: ". Path 'message.indicator', line 1, position 1023.
for
{"version":"35","timestamp":"2018-05-14T07:52:23.3347793Z","message":{"type":"msgType","value":{"id":"44","params":["AA","AC","BD"]},"indicator":{"data":"normal","indication":"no"}}}
Testing with MemoryStream:
var str = "{\"version\":\"35\",\"timestamp\":\"2018-05-14T07:52:23.3347793Z\",\"message\":{\"type\":\"msgType\",\"value\":{\"id\":\"44\",\"params\":[\"AA\",\"AC\",\"BD\"]},\"indicator\":{\"data\":\"normal\",\"indication\":\"no\"}}}";
str += str;
str += str;
str += str;
var ms = new MemoryStream();
ms.Write(str.GetBytes(), 0, str.GetBytes().Count());
ms.Position = 0;
Console.WriteLine(string.Format("StringLength:{0}", str.Length));
var streamReader = new StreamReader(ms);
var jsonTextReader = new JsonTextReader(streamReader)
{
CloseInput = false,
SupportMultipleContent = true
};
and then use the jsonTextReader as before.
I'm trying to get non-standard-format data from the clipboard using DataPackageView.GetDataAsync. I am stumped on converting the returned system.__ComObject to a string.
Here is the code:
var dataPackageView = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
if (dataPackageView.Contains("FileName"))
{
var data = await dataPackageView.GetDataAsync("FileName");
// How to convert data to string?
}
I am looking for a solution that will work with any non-standard clipboard format. "FileName" is an easily testable format as you can put it on the clipboard by copying a file in Windows Explorer.
In C++/Win32, I can get the clipboard data as follows:
OpenClipboard(nullptr);
UINT clipboarFormat = RegisterClipboardFormat(L"FileName");
HANDLE hData = GetClipboardData(clipboarFormat);
char * pszText = static_cast<char*>(GlobalLock(hData));
GlobalUnlock(hData);
CloseClipboard();
In C++, the clipboard data is just an array of bytes. It must be possible to get the same array of bytes in C#, but I have no clue on unwrapping/converting the system.__ComObject
Edit: Rephrasing the question:
How do I get a string or array of byes out of the system.__ComObject returned by dataPackageView.GetDataAsync(someFormat), where someFormat is an arbitrary clipboard format created by another application?
It is very clear to me how to get the data. The difficult part is using the data that is returned.
The accepted answer must show how to create a string or array of bytes from the "data" returned by
var data = await dataPackageView.GetDataAsync(someFormat);
if you know its a file you can use the following code
var content = Clipboard.GetContent();
IReadOnlyList<IStorageItem> files = await content.GetStorageItemsAsync();
var file = files.First() as StorageFile;
From MSDN article on StandardDataFormats
The DataPackage class supports a number of legacy formats for interoperability between Windows Store apps and desktop apps. To retrieve these formats, you pass one of the following strings to the DataPackageView.GetDataAsync method instead of a value from the StandardDataFormats class.
eg
var content = Clipboard.GetContent();
var data = await content.GetDataAsync("PenData"); //Stream for HGLOBAL corresponding to CF_PENDATA
This article explains how custom dataPackage objects are implemented.
http://www.minddriven.de/index.php/technology/dot-net/c-sharp/winrt-datapackage-custom-objects
The key is to cast the return value of dataPackageView.GetAsync() into an IRandomAccessStream
Here is something that works:
var dataPackageView = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
if (dataPackageView.Contains("FileName"))
{
var data = await dataPackageView.GetDataAsync("FileName");
// convert data to string
var data = await dataPackageView.GetDataAsync("FileName");
var dataObj = data as IRandomAccessStream;
var stream = dataObj.GetInputStreamAt(0);
IBuffer buff = new Windows.Storage.Streams.Buffer((uint)dataObj.Size);
await stream.ReadAsync(buff, (uint)dataObj.Size, InputStreamOptions.None);
var filePath = Encoding.ASCII.GetString(buff.ToArray());
filePath = filePath.Replace("\0","");//get rid of null characters
}
This should work for any custom data format, not just "FileName". If you do not require a string, you could just use the bytes available from the IRandomAccessStream.
The following code gets a Stream from a URI and will read in in chunks using a loop. Note that behind the specified URI is an online radio stream, which means there is no known size.
var uri = new Uri("http://*******", UriKind.Absolute);
var http = new HttpClient();
var stream = await http.GetStreamAsync(uri);
var buffer = new byte[65536];
while (true)
{
var read = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
Debug.WriteLine("Read: {0}", read);
}
Now while this works perfectly fine in a .NET 4.5 console app, this exactly same code does not work as expected in WinRT - it will read the first chunk, and when calling ReadAsync for the second time, it will just get stuck and never continue.
If I switch the URI to a file (of known size) everything works fine in both projects.
Any tips?
EDIT> note that this behaviour happens only on WP8.1. I just searched some more on SO and found that my question might be a duplicate of this one: WP8.1 HttpClient Stream got only 65536 bytes data If that is true, I will close my question
Use HttpClient.GetAsync() with HttpCompletionOption.ResponseHeadersRead. That returns when the headers are received, then do HttpResponse.Content.ReadAsInputStreamAsync().
Looks like your while loop is infinite. How are you ensuring that it's finite? Instead of while(true) make it something like this
var uri = new Uri("http://*******", UriKind.Absolute);
var http = new HttpClient();
var stream = http.GetStreamAsync(uri).Result;
using (var reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
var response = reader.ReadToEnd();
}
}