Web API Service running on Azure, with several web apis that return back an instance of an object
Shared data model project inside the same solution as the API Service
An MVC Web Client that runs on Azure as well and uses the same Shared data model project as the API Service
The MVC Client has views that let you input data
The MVC Client Controller/Action then sends the request to the Web API Service and pumps the output to another view
The Service gets the request and calls another 3rd party service to get a response to send back to the Web Client
The Service gets the response and attempts to serialize it into a .NET Class in the shared data model
The .NET class did NOT work initially because the incoming JSON formatted names were not the same as what was in the .NET Class. So I added JSONProperty Name Annotations and it works perfectly
However, when the Web Client tries to serialize the response JSON from the Service into the same .NET Class it fails (just leaves it null). Remembering that this Class is the same class and coming from the same DLL / Project as they are all in the same
solution as the Service uses
What I tried
In the Service, right after de-serializing the 3rd party data into my .NET class, I re-serialize it to a JSON string with JSONConvert and it DOES in fact have the property JSONProperty annotated names (good). So I do see serialization is using the JSON
names as expected
However, its still wrong on the client side.
On the client side, I created a new class, just for testing, that was just a copy and pass from my data models .NET class and then removed the JSONProperty Name attributes.
Immediately the Client property de-serialized the content into the "new" class instance
But this is not what I want. I don't want to have to have 2 sets of the exact same classes (as we have allot).
Is there any reason that the JSON.NET serializer (assuming that is the default for Web API now????) would ignore the JSONProperty attributes, when returning a response on a Web API?
Example
public class foobar {
private string flopper;
[JsonProperty("flopsee")]
public string Flopper (getter/setter)
}
The above works great when I first get the response from the 3rd Party into my Service. Or if I immediately re-serialize the newly created de-serialized class instance, after getting the 3rd party response.
foobar myfoobar = get response in json from 3rd party
As far as I can tell, the client and service class property names do not match. The service serializes to flopsee while the client serializes to
flopper.
Service
public class foobar
{
[JsonProperty("flopsee")]
public string Flopper { get; set; }
}
Client
public class foobarlocal
{
public string Flopper { get; set; }
}
The simplest solution is sharing the same POCO or specifically mapping the 3rd party type to your POCO. Then use the POCO in your application.
They do match, what I said was that I was originally use the exact same class, same shared data models project, so both the Service and the Client were using
But what I don't get it why they don't match is what I don't get...
The whole point of adding a JSONProperty("new name") is so that on incoming it knows what .NET member to populate with that data AND what to call it on the way out.
incoming JSON
{ "flopsee" : "me" }
becomes
foobar.Flopper because of the JSONProperty("flopsee"). Annotation.
When you immediately re-serialize foobar into a json string, you SEE { "flopsee" : "me" } which you should because you have the annotation
But when the Service returns this object foobar to the Client, the client sees "Flooper"... My question is WHY? The class that was being used (and the only one being used) was Foobar. and Foobar said JSONProperty("floopsee").
I only added the second class, to see if removing the annotation would work.
I thought Annotations were not just used on Incoming but also in Serialization.
public class foobar {
[JsonProperty("flopsee")]
public string Flopper { get; set; }
}
public class ValuesController : ApiController
{
public foobar Get()
{
return new foobar { Flopper = "Hello World" };
}
Results
{
"flopsee": "Hello World"
}
Still your two classes have property names that do not match. I assume that's the problem. Or you have a bug in the code that we cannot see on the forum.
I created a repro and per say I think I see what is happening, I just don't know how to fix it as its not in my code doing in.
I created a new set of classes in my Shared Data Model project that both Client and Service use.
[Serializable]
publicclassDataFromExternal
{
#region private properties
privatestring dataContext;
private TheData[] theData;
privatestring oDataNextPage;
#endregion
#region public properties
[JsonProperty("@odata.context")]
publicstring DataContext {
get => dataContext;
set => dataContext = value; }
[JsonProperty("value")]
public TheData[] TheData {
get => theData;
set => theData = value; }
[JsonProperty("@odata.nextLink")]
publicstring ODataNextPage {
get => oDataNextPage;
set => oDataNextPage = value; }
#endregion
}
I have a secondary class to support the value property below
[Serializable]
publicclassTheData
{
#region private Properties
privatestring id;
privatestring createdDateTime;
#endregion
#region public properties
[JsonProperty("id")]
publicstring Id {
get => id;
set => id = value; }
[JsonProperty("createdDateTime")]
publicstring CreatedDateTime {
get => createdDateTime;
set => createdDateTime = value; }
#endregion
}
In my Web API Service I created a Web API below. You will notice that there is a specific line, that re-serializes the result from the 3rd party just to see if the serializations matches what I need. It does.
// This JSON does NOT have @data.context, it has dataContext, which is the "private" named version of my .Net class.., so it renamed it when it was passing it across the web.. but didn't rename it locally when I did this in the Service
So I still don't understand, why Locally in the Server Service, I could serialize the object and its names were @data.context, but what gets passed across the wire is dataContext.
Based on your code, first, you need to compare the json you request from webapi with locally in the Server Service requested json to see if there is a difference between them.
Second, you need to check whether the version of "Newtonsoft.Json;" referenced in your client and model is the same.
Best Regards,
YongQing.
MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue.
If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.
I already compare them, as per the notes in the code. The JSON coming from the 3rd party has their names in it. In the Service, if you use it to create an instance of the .NET class it works. If you immediately take that .NET class instance and turn it back
into JSON, it has the 3rd party, JSONProperty annotated names in it. Which is a line of code above to demonstrate that.
In the Client first thing I do is look at the JSON, and it does not have the 3rd party names in it, it has the .NET names in it, but since the class is expecting the names to match the 3rd party (a.k.a JSONProperty names), it will not deserialize and fails.
All projects (Server, Models, Client) are in the same solution. All have the same JSON version. All are running on the same dev machine at the same time. Although I can repro this on any machine and have done so on several.
You have to understand that the community cannot reproduce this issue. I have built and support many application that make calls to remote services from API, WCF, ASMX, etc. I don't have this problem. Most likely, you have a design bug. That's why deploying
the same code causes the same results.
From my experience, it is uncommon for a remote server to use the same property names that my application uses and the shape of the data is usually different. I like to build a mapping layer that takes the remote service response and uses it to populate
my POCO. The POCO I designed for my application that has all the names I expect. Same idea for sending data to the remote service. I'll take my POCO and convert it to the format the remote service expects. Being that your remote service appears to be an
OData service, mapping is probably the best option.
This is a single Project that is MVC/Web API so the Client and Server is in the same project
The datamodel project is merely there to "emulate" what my real stuff is, but is just a simple data model that has JSONProperty name @ names.
Set the RepoAllInOne as your start up.
Put a break point in public async Task<ActionResult> RepoOfSerializationIssue()
The first thing it does is creates an instance of the class and serializes it in the Web Client Controller. You can see the JSONProperty names in the JSON it creates.
Then it calls the Server, and inside the Server controller it fakes an Asyn call, takes a JSON that has the @names in it, and serializes it into a Class instance.
Then it de-serializes it, just like I did in the Client as a test, and you can see it has the @ names.
Then it passed back the Object Class
in the Client, you can see that the returned JSON does NOT have the @ names in it, which it should.
So something happens in the serialization/deserialization from the call to the Server to the Response.
You should not use the "ReadAsStringAsync()" method. You can try to use "ReadAsAsync<RepoDataMaster>()" to get the
object, and then serialize and deserialize, so that it can meet your needs.
Member
1 Points
7 Posts
Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 05:22 PM|M M E G FL|LINK
Hello, I have the following situation
What I tried
But this is not what I want. I don't want to have to have 2 sets of the exact same classes (as we have allot).
Is there any reason that the JSON.NET serializer (assuming that is the default for Web API now????) would ignore the JSONProperty attributes, when returning a response on a Web API?
Example
public class foobar {
private string flopper;
[JsonProperty("flopsee")]
public string Flopper (getter/setter)
}
The above works great when I first get the response from the 3rd Party into my Service. Or if I immediately re-serialize the newly created de-serialized class instance, after getting the 3rd party response.
foobar myfoobar = get response in json from 3rd party
it works great
string jsonstring = JsonConvert.Serialize(myfoobar)
I can see the names in the string properly
foobar myfoobar2 = JsonConvert.Deserialize<foobar>(jsonstring)
again works great
But on the client
foobar myfoobarclient = get response in json from web service
Even though the JSON response is there and I can see, and see it has NO annotated names just regular . NET Names coming in
myfoobarclient == null
So I created a new local version of the class without the JSONProperty name annotation, since I didn't see it come across the Wire this way
public class foobarlocal {
private string flopper;
//removed JSONProperty
public string Flopper (getter/setter)
}
Now this works
foobarlocal myfoobarclient = get response in json from web service
But I wanted foobar = myfoobar to work... not have to create another class
Thanks,
All-Star
53121 Points
23672 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 07:43 PM|mgebhard|LINK
As far as I can tell, the client and service class property names do not match. The service serializes to flopsee while the client serializes to flopper.
Service
public class foobar { [JsonProperty("flopsee")] public string Flopper { get; set; } }
Client
public class foobarlocal { public string Flopper { get; set; } }
The simplest solution is sharing the same POCO or specifically mapping the 3rd party type to your POCO. Then use the POCO in your application.
Member
1 Points
7 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 08:17 PM|M M E G FL|LINK
HI mgebhard
Thanks for the reply.
They do match, what I said was that I was originally use the exact same class, same shared data models project, so both the Service and the Client were using
public class foobar { [JsonProperty("flopsee")] public string Flopper { get; set; } }
But that I then created a local different version, without the JsonProperty naming annotation, which then allowed it to work.
The 3rd party calls it flopsee.
.NET calls it Flopper
Serialization should call it flopsee, it doesn't, not when it passed it from Service to Client.
All-Star
58254 Points
15678 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 08:21 PM|bruce (sqlwork.com)|LINK
if the internal json names don't match, the external json names, then you need two models or a custom serializer.
Member
1 Points
7 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 08:26 PM|M M E G FL|LINK
hi Bruce
Thanks!!
But what I don't get it why they don't match is what I don't get...
The whole point of adding a JSONProperty("new name") is so that on incoming it knows what .NET member to populate with that data AND what to call it on the way out.
incoming JSON
{ "flopsee" : "me" }
becomes
foobar.Flopper because of the JSONProperty("flopsee"). Annotation.
When you immediately re-serialize foobar into a json string, you SEE { "flopsee" : "me" } which you should because you have the annotation
But when the Service returns this object foobar to the Client, the client sees "Flooper"... My question is WHY? The class that was being used (and the only one being used) was Foobar. and Foobar said JSONProperty("floopsee").
I only added the second class, to see if removing the annotation would work.
I thought Annotations were not just used on Incoming but also in Serialization.
All-Star
53121 Points
23672 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 08:48 PM|mgebhard|LINK
I cannot reproduce this issue.
Results
Still your two classes have property names that do not match. I assume that's the problem. Or you have a bug in the code that we cannot see on the forum.
Member
1 Points
7 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 20, 2020 09:49 PM|M M E G FL|LINK
Hello again,
I created a repro and per say I think I see what is happening, I just don't know how to fix it as its not in my code doing in.
I created a new set of classes in my Shared Data Model project that both Client and Service use.
[Serializable]
public class DataFromEx
ternal
{
#region private properties
private string dataContext;
private TheData[] theData;
private string oDataNextPage;
#endregion
#region public properties
[JsonProperty("@odata.context")]
public string DataContext { get => dataContext; set => dataContext = value; }
[JsonProperty("value")]
public TheData[] TheData { get => theData; set => theData = value; }
[JsonProperty("@odata.nextLink")]
public string ODataNextPage { get => oDataNextPage; set => oDataNextPage = value; }
#endregion
}
I have a secondary class to support the value property below
[Serializable]
public class TheData
{
#region private Properties
private string id;
private string createdDateTime;
#endregion
#region public properties
[JsonProperty("id")]
public string Id { get => id; set => id = value; }
[JsonProperty("createdDateTime")]
public string CreatedDateTime { get => createdDateTime; set => createdDateTime = value; }
#endregion
}
In my Web API Service I created a Web API below. You will notice that there is a specific line, that re-serializes the result from the 3rd party just to see if the serializations matches what I need. It does.
public async Task<DataFromExternal> GetDataFrom3rdparty([FromBody] WebClientRequest webClientRequest)
{
try
{
string AccessToken = await GetToken();
Client client = new Client();
DataFromExternal thedata = await client.GetDataFrom3rdparty(AccessToken, webClientRequest.Version, webClientRequest.AccessType, null);
// The next line of code was JUST to see if I re-serialized it would it contain the @data.context etc as names in the JSON and it does.
string thedatajson = JsonConvert.SerializeObject(thedata);
return thedata; // return the object to the client
}
catch (Exception ex)
{
throw ex;
}
}
Now in my Web Client
public async Task<ActionResult> GetDataFrom3rdparty(WebClientRequest webClientRequest)
{
try
{
DataFromExternal thedata = null;
AuthenticationResult result = await GetToken();
if (result != null)
{
// This JSON does NOT have @data.context, it has dataContext, which is the "private" named version of my .Net class.., so it renamed it when it was passing it across the web.. but didn't rename it locally when I did this in the Service
string json = await ExecutePlannerServiceCall(result.AccessToken, "GetDataFrom3rdparty", webClientRequest);
if (!string.IsNullOrEmpty(json))
{
thedata = JsonConvert.DeserializeObject<DataFromExternal>(json);
ViewBag.thedata = thedata;
}
else
{
ViewBag.grothedataups = null;
}
}
return PartialView(thedata);
}
catch (Exception ex)
{
ViewBag.Message = ex;
return View("Error");
}
}
So I still don't understand, why Locally in the Server Service, I could serialize the object and its names were @data.context, but what gets passed across the wire is dataContext.
Why does it lose the JSONProperty name?
Contributor
3710 Points
1043 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 21, 2020 09:34 AM|Yongqing Yu|LINK
Hi,
Based on your code, first, you need to compare the json you request from webapi with locally in the Server Service requested json to see if there is a difference between them.
Second, you need to check whether the version of "Newtonsoft.Json;" referenced in your client and model is the same.
Best Regards,
YongQing.
Please remember to click "Mark as Answer" the responses that resolved your issue.
If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.
Member
1 Points
7 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 21, 2020 01:35 PM|M M E G FL|LINK
Thanks Yongqing
I already compare them, as per the notes in the code. The JSON coming from the 3rd party has their names in it. In the Service, if you use it to create an instance of the .NET class it works. If you immediately take that .NET class instance and turn it back into JSON, it has the 3rd party, JSONProperty annotated names in it. Which is a line of code above to demonstrate that.
In the Client first thing I do is look at the JSON, and it does not have the 3rd party names in it, it has the .NET names in it, but since the class is expecting the names to match the 3rd party (a.k.a JSONProperty names), it will not deserialize and fails.
All projects (Server, Models, Client) are in the same solution. All have the same JSON version. All are running on the same dev machine at the same time. Although I can repro this on any machine and have done so on several.
Cheers,
All-Star
53121 Points
23672 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 21, 2020 01:48 PM|mgebhard|LINK
You have to understand that the community cannot reproduce this issue. I have built and support many application that make calls to remote services from API, WCF, ASMX, etc. I don't have this problem. Most likely, you have a design bug. That's why deploying the same code causes the same results.
From my experience, it is uncommon for a remote server to use the same property names that my application uses and the shape of the data is usually different. I like to build a mapping layer that takes the remote service response and uses it to populate my POCO. The POCO I designed for my application that has all the names I expect. Same idea for sending data to the remote service. I'll take my POCO and convert it to the format the remote service expects. Being that your remote service appears to be an OData service, mapping is probably the best option.
Member
1 Points
7 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 21, 2020 04:17 PM|M M E G FL|LINK
Here is a full repro, would love if you will take this and try it.
Load it, (one zip has the packages, one does not). Run and step through. Super simple easy repro.
Link to the Project Zips
Put a break point in public async Task<ActionResult> RepoOfSerializationIssue()
The first thing it does is creates an instance of the class and serializes it in the Web Client Controller. You can see the JSONProperty names in the JSON it creates.
Then it calls the Server, and inside the Server controller it fakes an Asyn call, takes a JSON that has the @names in it, and serializes it into a Class instance.
Then it de-serializes it, just like I did in the Client as a test, and you can see it has the @ names.
Then it passed back the Object Class
in the Client, you can see that the returned JSON does NOT have the @ names in it, which it should.
So something happens in the serialization/deserialization from the call to the Server to the Response.
Thanks,
Contributor
3710 Points
1043 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 22, 2020 07:01 AM|Yongqing Yu|LINK
Hi,
You should not use the "ReadAsStringAsync()" method. You can try to use "ReadAsAsync<RepoDataMaster>()" to get the object, and then serialize and deserialize, so that it can meet your needs.
More details, you could refer to below code:
Best Regards,
YongQing.
Please remember to click "Mark as Answer" the responses that resolved your issue.
If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.
Member
1 Points
7 Posts
Re: Serialization Loss of JSONProperty Name annotations when returning from Web API
Apr 22, 2020 01:13 PM|M M E G FL|LINK
Thank you!!!!!!!