I have the following code:
private async Task ResponseReceived(Stream inputStream) {
using var reader = new StreamReader(inputStream);
var message = await reader.ReadToEndAsync();
var obj = JsonSerializer.Deserialize<BaseMessage>(message);
Console.WriteLine($"Message Type Received: {obj?.EventType}");
}
To which all message classes extend. The BaseMessage class contains a single property: EventType that exists in every json string deserialization.
public class BaseMessage
{
[JsonPropertyName("e")] public string EventType { get; init; }
}
The EventType provides information on the received json structure, to which I want to be able to trigger an event which publishes the correct object type based on the EventType.
The simplest solution I can think of is to do a switch case and deserialize a second time into the correct type, and then publish the event. However this poses 2 problems:
Switch case, requires coding for all the possible EventTypes (which is at least 20 currently, and could expand to many more).
Deserialization a second time, creates additional overhead in processing (both in memory and speed) where it is processing thousands of messages per second.
Is there a better way to optimize this either via a design pattern, or other way to handle it to avoid the above two concerns? I am currently using .NET Core 5, and System.Text.Json (rather than Newtonsoft Json library). I would prefer to stick with the Microsoft library, but am flexible if need to switch to Newtonsoft.
for the first problem, if EventType contains also the namespace, you can deserialize with
var objBase = JsonSerializer.Deserialize<BaseMessage>(message);
var objSpecific = JsonSerializer.Deserialize(message, Type.GetType(objBase.EventType));
so you don't need a switch.
for the second problem there is a solution, but it's breakable.
You can search for the class type inside the message.
var message = JsonSerializer.Serialize(new BaseMessage() { EventType = (typeof(A)).ToString() });
var typeString = Regex.Match(message, "\"e\":\"(.*?)\"").Groups[1];
var objSpecific = JsonSerializer.Deserialize(message, Type.GetType(typeString.Value));
if you are not forced I do not recommend it, because you can change the property name for example and you can find an error at run time or with a test.
In an MVC project, I have a method on a hub similar to this:
public string Foo() { return DoCrazyThingThatReturnsJson(); }
Unfortunately SignalR (or something) takes the encoded JSON string and happily encodes it, then returns it, so browser be like LOLWTF. Is there a way to skip this second encoding?
Looking at this here:
We assume that any ArraySegment is already JSON serialized
it seems like something like this may work (note that I haven't tried it, so no promises):
string jsonString = ...; // your serialized data
var jsonBytes = new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsonString));
Clients.Caller.sendJson(jsonBytes);
(The above only works for PersistentConnection because of what constitutes a value - for hubs, the data is wrapped in a container with the RPC info; I'm only leaving this in case using PersistentConnection is an option for you)
Another idea might be creating a container class for a JSON string, then using a custom converter that simply writes the string as-is, then register like this in the Signalr startup code:
var serializer = new JsonSerializer();
serializer.Converters.Add(new PreserializedConverter());
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);
Currently, I have some issues. I'm using C# with Json.NET. The issue is that I always get:
{"Unexpected character encountered while parsing value: e. Path '', line 0, position 0."}
So the way I'm using Json.NET is the following. I have a Class which should be saved. The class looks like this:
public class stats
{
public string time { get; set; }
public string value { get; set; }
}
public class ViewerStatsFormat
{
public List<stats> viewerstats { get; set; }
public String version { get; set; }
public ViewerStatsFormat(bool chk)
{
this.viewerstats = new List<stats>();
}
}
One object of this class will be filled and saved with:
File.WriteAllText(tmpfile, JsonConvert.SerializeObject(current), Encoding.UTF8);
The saving part works fine and the file exists and is filled. After that the file will be read back into the class with:
try
{
ViewerStatsFormat current = JsonConvert.DeserializeObject<ViewerStatsFormat>(tmpfile);
//otherstuff
}
catch(Exception ex)
{
//error loging stuff
}
Now on the current= line comes the exception:
{"Unexpected character encountered while parsing value: e. Path '', line 0, position 0."}
I don't know why this comes. The JSON file is the following -> Click me I am the JSON link
Does anyone have any ideas?
Possibly you are not passing JSON to DeserializeObject.
It looks like from File.WriteAllText(tmpfile,... that type of tmpfile is string that contain path to a file. JsonConvert.DeserializeObject takes JSON value, not file path - so it fails trying to convert something like #"c:\temp\fooo" - which is clearly not JSON.
I solved the problem with these online tools:
To check if the Json structure is OKAY: http://jsonlint.com/
To generate my Object class from my Json structure: https://www.jsonutils.com/
The simple code:
RootObject rootObj= JsonConvert.DeserializeObject<RootObject>(File.ReadAllText(pathFile));
In my case, the file containing JSON string had BOM. Once I removed BOM the problem was solved.
I experienced the same error in my Xamarin.Android solution.
I verified that my JSON was correct, and noticed that the error only appeared when I ran the app as a Release build.
It turned out that the Linker was removing a library from Newtonsoft.JSON, causing the JSON to be parsed incorrectly.
I fixed the error by adding Newtonsoft.Json to the Ignore assemblies setting in the Android Build Configuration (screen shot below)
JSON Parsing Code
static readonly JsonSerializer _serializer = new JsonSerializer();
static readonly HttpClient _client = new HttpClient();
static async Task<T> GetDataObjectFromAPI<T>(string apiUrl)
{
using (var stream = await _client.GetStreamAsync(apiUrl).ConfigureAwait(false))
using (var reader = new StreamReader(stream))
using (var json = new JsonTextReader(reader))
{
if (json == null)
return default(T);
return _serializer.Deserialize<T>(json);
}
}
Visual Studio Mac Screenshot
Visual Studio Screenshot
I have also encountered this error for a Web API (.Net Core 3.0) action that was binding to a string instead to an object or a JObject. The JSON was correct, but the binder tried to get a string from the JSON structure and failed.
So, instead of:
[HttpPost("[action]")]
public object Search([FromBody] string data)
I had to use the more specific:
[HttpPost("[action]")]
public object Search([FromBody] JObject data)
This issue is related to Byte Order Mark in the JSON file. JSON file is not encoded as UTF8 encoding data when saved. Using File.ReadAllText(pathFile) fix this issue.
When we are operating on Byte data and converting that to string and then passing to JsonConvert.DeserializeObject, we can use UTF32 encoding to get the string.
byte[] docBytes = File.ReadAllBytes(filePath);
string jsonString = Encoding.UTF32.GetString(docBytes);
I had the same problem with webapi in ASP.NET core, in my case it was because my application needs authentication, then it assigns the annotation [AllowAnonymous] and it worked.
[AllowAnonymous]
public async Task <IList <IServic >> GetServices () {
}
I ran into this issue and it ended up being because of BOM characters in my input string.
Here's what I ended up doing:
String.Trim(new char[] { '\uFEFF', '\u200B' });
This resolved the issue for me.
In my case, I was getting an error on JsonConvert.PopulateObject().
My request was returning JSON that was wrapped in an extra pair of '[ ]' brackets, making my result an array of one object rather than just an object. Here's what I did to get inside these brackets (only for that type of model):
T jsonResponse = new T();
var settings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.DateTimeOffset,
NullValueHandling = NullValueHandling.Ignore,
};
var jRslt = response.Content.ReadAsStringAsync().Result;
if (jsonResponse.GetType() == typeof(myProject.Models.MyModel))
{
var dobj = JsonConvert.DeserializeObject<MyModel[]>(jRslt);
var y = dobj.First();
var szObj = JsonConvert.SerializeObject(y);
JsonConvert.PopulateObject(szObj, jsonResponse, settings);
}
else
{
JsonConvert.PopulateObject(jRslt, jsonResponse);
}
If you are using downloading data using url...may need to use
var result = client.DownloadData(url);
In my scenario I had a slightly different message, where the line and position were not zero.
E. Path 'job[0].name', line 1, position 12.
This was the top Google answer for the message I quoted.
This came about because I had called a program from the Windows command line, passing JSON as a parameter.
When I reviewed the args in my program, all the double quotes got stripped.
You have to reconstitute them.
I posted a solution here. Though it could probably be enhanced with a Regex.
I had a similar error and thought I'd answer in case anyone was having something similar. I was looping over a directory of json files and deserializing them but was getting this same error.
The problem was that it was trying to grab hidden files as well. Make sure the file you're passing in is a .json file. I'm guessing it'll handle text as well. Hope this helps.
I had simular problem. In my case the problem was in DateTime format. It was just numbers and it is also know as EpochFormat or UnixTimestamp.
A part from my JSON:
"direction": "outbound",
"date_archive": 1554691800224,
"date_doc": 1524700800000,
"date_sent": 1524704189000,
"date_received": 1524704189000,
"date_store_till": 1712544600224,
So I've used an attribute like this:
[JsonProperty("date_received")]
[JsonConverter(typeof(MicrosecondEpochConverter))]
public DateTime? DateReceived { get; set; }
You can find MicrosecondEpochConverter code here: https://stackoverflow.com/a/19972214/4324624
I faced similar error message in Xamarin forms when sending request to webApi to get a Token,
Make sure all keys (key : value) (ex.'username', 'password', 'grant_type') in the Json file are exactly what the webApi expecting, otherwise it fires this exception.
Unhandled Exception: Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: <. Path '', line 0, position 0
Please check the model you shared between client and server is same. sometimes you get this error when you not updated the Api version and it returns a updated model, but you still have an old one. Sometimes you get what you serialize/deserialize is not a valid JSON.
In my case, it was the lack of a default parameterless constructor !!!
In my case, I was calling the async service method without using await, so before Task is completed I was trying to return the result!
Suppose this is your json
{
"date":"11/05/2016",
"venue": "{\"ID\":12,\"CITY\":Delhi}"
}
if you again want deserialize venue, modify json as below
{
"date":"11/05/2016",
"venue": "{\"ID\":\"12\",\"CITY\":\"Delhi\"}"
}
then try to deserialize to respective class by taking the value of venue
This error occurs when we parse json content to model object. Json content type is string.
For example:
https://dotnetfiddle.net/uFClKj
Some times, an api that we call may return an error. If we do not check the response status, but proceed to parse the response to model, this issue will occur.
When I encountered a similar problem, I fixed it by substituting &mode=xml for &mode=json in the request.
I've created a dictionary like so:
public Dictionary<string, string> myValues = new Dictionary<string, string>();
Then in my start method I'm filling out my dictionary like so:
void Start()
{
myValues.Add("device one", "11111111111111");
}
And what I'm trying to do is check the value that I've created in my dictionary, in the above example it would be the 1111111111111, against a string value that is being read in remotely. The check I'm doing is this:
void Update ()
{
// Spawn a model based on the signal the ipad has recievied.
if( myValues["device one"] == outputContent.text)
{
Instantiate(model1, new Vector3(-2.5f, 3.0f,0), Quaternion.identity);
}
}
The message is getting parsed from a wrapper class that takes information from native ipad stuff and passes it direct to unity. The method for getting the message is this:
private void AppendString(string message)
{
outputContent.text += "\n" + message;
}
Now, the thing is, the message getting passed works. When I run my code, the screen gets filled with the information I want. But when I try to check the values I've stored against the ones getting sent in, nothing happens.
How I got my initial hard coded value was by first reading them in from the AppendString method. And I've double checked them to make sure I've got the correct information down.
Can someone please tell me if I'm comparing the values held within my dictionary, to those being read in, is correct?
There's a couple of 'suspicious' spots.
Here, you add \n, are you sure that's included in the dictionary's value?
outputContent.text += "\n" + message;
Also, make sure you compare the strings properly. It may even be a capitalization problem, something that's bitten me many times. Try for instance:
if(String.Equals(myValues["device one"], outputContent.text, StringComparison.OrdinalIgnoreCase)
better replace following line
if( myValues["device one"] == outputContent.text)
with:
if( myValues["device one"].Equals(outputContent.text))
I had a webmethod working which returned a byte array to the caller:
public byte[] DownloadPDF(string URI)
I had to change this to return another output (a string). So, I decided to change the method completely by now returning void and having 3 parameters like this:
public void DownloadFile(string URI, out byte[] docContents, out string returnFiletype)
My web service compiles correctly but I suspect something is wrong with the 2nd parameter (i.e. the byte array) because when I "Add Web Reference" and build my proxy class, the method has only 2 parameters, not 3):
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/DownloadFile", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("docContents", DataType="base64Binary")]
public byte[] DownloadFile(string URI, out string returnFiletype) {
object[] results = this.Invoke("DownloadFile", new object[] {
URI});
returnFiletype = ((string)(results[1]));
return ((byte[])(results[0]));
}
I don't see why my 2nd parameter, the byte array, is being ignored, but it appears to be the source of the problem.
This of course messes me up in the web client app where I get an error message at compile time:
No overload for method 'DownloadFile' takes '3' arguments
Here is the code in the web client where I need to pass 3 arguments:
myBrokerASMXProxy.ASMXProxy.FileService client = new myASMXProxy.ASMXProxy.FileService();
byte[] fileDataBytes;
string fileType;
client.DownloadFile(URI, fileDataBytes, fileType);
I am thinking of changing it back to return a byte array and add just a single "out" parameter but I thought I should ask you experts about this and in general, what is the best practice for handling multiple output requirements.
Why don't your try putting this signature:
public bool DownloadFile(string URI, out byte[] docContents, out string returnFiletype)
To see what happens? I agree with Jon Skeet, but you can still can return a bool with the result of the operation
The byte array isn't being ignored - it's being put as the return type instead. I don't know why it's doing that, but it makes more sense in my view. I wouldn't use out parameters in a void method. I suspect the proxy generator just takes any method with out parameters and turns the first one into a return type.