I have a WCF Rest Service. I would like to use it from Android device. To do this I have chosen gson library. Everything seemed to work fine until I wanted to return from my service Object Containing Map. Gson serializes it (and as I am assuming also tries to deserialize from it) as something like:
{"org.Mob.ComplexObject#3dac2f9c":"TAX1","org.Mob.ComplexObject#7369ca65":"TAX2"}
But the json sent by my service looks like:
{"Key":
{"DefaultValue":"True",
"Description":null,
"DisplayName":"Custom Boolean",
"FieldType":0,
"Id":6,
"IsReadOnly":false,
"IsRequired":false,
"MaxLength":null,
"Name":"BoolVal",
"ParamType":0},
"Value":"True"},
{"Key":
{"DefaultValue":"",
"Description":null,
"DisplayName":"Custom Text",
"FieldType":4,
"Id":7,
"IsReadOnly":false,
"IsRequired":true,
"MaxLength":16,
"Name":"TextVal",
"ParamType":0},
"Value":"sda"}
}
Gson does not have a built-in feature to serialize a Java map into JSON formatted as desired. (The feature to handle complex map keys does not address this.) Custom serialization/deserialization processing is necessary.
The other way to do it could be adding you own implementation of Map / Dictionary which extends ArrayList, and in fact is a List of KVPair, where KVPair looks like:
class KVPair<K,V>{
K Key;
V Value;
}
I don't know if it's elegant, but it worked in my case.
Related
I have an API with two clients: OldClient and newClient. I currently have this in Startup.cs so my json responses are serialized as PascalCase, i.e. as per all my .net objects which have first letter capitalized.
services.AddControllers().AddJsonOptions(jsonOptions =>
{
// So json output is like 'SomeId' instead of 'someId':
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;
...
}
OldClient loves this format. However, newClient would really prefer camelCase.
Is there a way I can configure my app to respond with camelCase for newClient requests and PascalCase for OldClient requests? newClient can send a header to indicate that it wants camelCase.
You can check out this issue on aspnetcore's github page
The possibility of using specific JSON Serializer Options on a per-controller basis hasn't been implemented yet. It has been moved to ".NET 8 planning", which means it's still a ways-away.
Meanwhile, you could work around this issue by:
For data reception and model-binding, you could create a Custom ModelBinder by implementing IModelBinder interface in a ModelBinderAttribute in order to utilize your specific JSON Serialization options. Then, you could simply add the attribute to the endpoints where you need it.
For data responses, you could simply use:
return new JsonResult([object], [CustomJSONSerializationSettings]);
It's quite annoying to have to modify these per-endpoint, but it seems like it's the only way until the feature is added in .net 8 (if we're lucky).
We are using https://github.com/Azure/amqpnetlite from the Azure team.
The description says we have to use the AmqpContract and AmqpMember attribute classes over the message bus like ActiveMQ.
We also have a lot of classes and we want to migrate to this Amqp library, but is it possible to use the serializer without those attributes and to use our POCO classes directly?
Another workaround could(?) be to serialize or classes into a json and use a wrapper class to send our payload:
[AmqpContract]
public class OurMessage
{
[AmqpMember]
public string JsonPayload {get;set;}
}
or should we avoid this workaround?
The POCO classes as you mentioned are not supported right now. You will need to pick another serializer. Once you have the JSON string or a byte array, you do not need to define a wrapper class. Just directly create an AMQP message with the payload and get it back from the message body on the receiver side.
// sender
var msg = new Message(jsonPayload);
sender.Send(msg);
// receiver
var msg2 = receiver.Receive();
var jsonPayload = msg2.GetBody<string>();
Same thing can be done for byte array.
The AMQP serializer is designed to be simple and everything has to be explicitly annotated. For most cases this is possible and it helps to avoid double encoding and enable easier inter-op with other AMQP clients.
Is it possible to change the default serialisation of C# poco's for documentDb? The id-property for instance seem to be required to be lower case, but the default serialisation of the Id property is upper case. Ideally we would like all json properties to start with lower case characters. The only way we found so far is to decorate the properties with [JsonProperty(PropertyName = "id")] but it's not very elegant.
Currently you can't change the default serializer of DocumentDB, you can however serialize it using your own library or JSON.NET and store the JSON string to the collection by doing:
await client.CreateDocumentAsync(collectionLink, Resource.LoadFrom<Document>(stream));
where stream a stream to your json string (can be from a file, or from an in-memory string, etc).
You can find more info on the internet archive's edition of my blog post, which used to reside here
Edit: JSON serializer settings is supported in the DocumentDB .NET SDK 1.16.0+. https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-sdk-dotnet
Here a couple ways to get lower-case or camel-case properties in your DocumentDB document:
Use [JsonProperty(PropertyName = "id")] as you mentioned.
Change the C# property in the POCO to lower case.
Have your POCO extend Microsoft.Azure.Documents.Document from the DocumentDB .NET Library, which has an Id property (that I believe uses [JsonProperty(PropertyName = "id")] behind the scenes).
Instead of using the default serializer, you can use the Json.NET library to serialize using it's camel-case resolver. Mats Karlsson has a pretty good blog post on this here: http://www.matskarlsson.se/blog/serialize-net-objects-as-camelcase-json
Edit: JSON serializer settings is supported in the DocumentDB .NET SDK 1.16.0+. https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-sdk-dotnet
I'm building an ASP.NET (2.0, no, I can't change it) site with NHibernate, and have a custom JSON converter so I can not-serialize properties I want hidden from the client. This lets me just return the objects, and never have to worry about their serialized values - they're always secure.
Unfortunately, it appears that if I use query.FutureValue<class>(), the object that gets serialized is first the NHibernate.Impl.FutureValue<class> and not my entity, which means I get JSON that looks like this if I throw it in a dictionary and return it to the client:
{key: { Value: { /* my serialized object properties */ } }
Previously I discovered that I can't get any interfaces to work in ASP's JavaScriptConverter implementations... only regular or abstract classes. So returning typeof(IFutureValue<MyBaseClass>) as a supported type means my converter is completely ignored. I can catch MyBaseClass, because I refactored things earlier to use an abstract base instead of an interface, but not the interface.
And then I discover that the FutureValue implementation in .Impl is internal to the assembly, or some other such nonsense that only serves to make my .NET experience even more painful. So I can't use typeof(FutureValue<MyBaseClass>) to handle it all, because FutureValue exists only in my debugging sessions.
Is there a way to get the class type out of the assembly? Or a way to convince ASP that interfaces do in fact have uses? Or might there be some superclass I can access that would let me get around the whole issue?
Help! I like my Futures, it lets me batch a whole heck-ton of calls at once!
(if something isn't clear, or you want more code, by all means, ask! I can post quite a bit.)
If I'm understanding you correctly, it seems you are mixing things a together a little bit.
It sounds like you're trying to serialize an instance of query.FutureValue<class>(), which unsurprisingly gives you just that: a JSON object where the Value fields has JSON representing your entity.
To me it sounds like you really want to just serialize query.FutureValue<class>().Value.
Using NHibernate futures like this gives you little benefit though, so you're probably after something like:
var future1 = query1.FutureValue<SomeEntity>();
var future2 = query2.FutureValue<AnotherEntity>();
var json1 = serializer.Serialize(future1.Value); //<BAM! Multi-query gets fired!
var json2 = serializer.Serialize(future2.Value);
Does that make sense?
What options are there for serialization when returning instances of custom classes from a WebService?
We have some classes with a number of child collection class properties as well as other properties that may or may not be set depending on usage. These objects are returned from an ASP.NET .asmx WebService decorated with the ScriptService attribute, so are serialized via JSON serialization when returned by the various WebMethods.
The problem is that the out of the box serialization returns all public properties, regardless of whether or not they are used, as well as returning class name and other information in a more verbose manner than would be desired if you wanted to limit the amount of traffic.
Currently, for the classes being returned we have added custom javascript converters that handle the JSON serializtion, and added them to the web.config as below:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization>
<converters>
<add name="CustomClassConverter" type="Namespace.CustomClassConverter" />
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
But this requires a custom converter for each class. Is there any other way to change the out of the box JSON serialization, either through extending the service, creating a custom serializer or the like?
Follow Up
#marxidad:
We are using the DataContractJsonSerializer class in other applications, however I have been unable to figure out how to apply it to these services. Here's an example of how the services are set-up:
[ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public CustomClass GetCustomClassMethod
{
return new customClass();
}
}
The WebMethods are called by javascript and return data serialized in JSON. The only method we have been able to change the serialization is to use the javascript converters as referenced above?
Is there a way to tell the WebService to use a custom DataContractJsonSerializer? Whether it be by web.config configuration, decorating the service with attributes, etc.?
Update
Well, we couldn't find any way to switch the out of the box JavaScriptSerializer except for creating individual JavaScriptConverters as above.
What we did on that end to prevent having to create a separate converter was create a generic JavaScriptConverter. We added an empty interface to the classes we wanted handled and the SupportedTypes which is called on web-service start-up uses reflection to find any types that implement the interface kind of like this:
public override IEnumerable<Type> SupportedTypes
{
get
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AssemblyBuilder dynamicAssemblyCheck = assembly as AssemblyBuilder;
if (dynamicAssemblyCheck == null)
{
foreach (Type type in assembly.GetExportedTypes())
{
if (typeof(ICustomClass).IsAssignableFrom(type))
{
yield return type;
}
}
}
}
}
}
The actual implementation is a bit different so that the type are cached, and we will likely refactor it to use custom attributes rather than an empty interface.
However with this, we ran into a slightly different problem when dealing with custom collections. These typically just extend a generic list, but the custom classes are used instead of the List<> itself because there is generally custom logic, sorting etc. in the collection classes.
The problem is that the Serialize method for a JavaScriptConverter returns a dictionary which is serialized into JSON as name value pairs with the associated type, whereas a list is returned as an array. So the collection classes could not be easily serialized using the converter. The solution for this was to just not include those types in the converter's SupportedTypes and they serialize perfectly as lists.
So, serialization works, but when you try to pass these objects the other way as a parameter for a web service call, the deserialization breaks, because they can't be the input is treated as a list of string/object dictionaries, which can't be converted to a list of whatever custom class the collection contains. The only way we could find to deal with this is to create a generic class that is a list of string/object dictionaries which then converts the list to the appropriate custom collection class, and then changing any web service parameters to use the generic class instead.
I'm sure there are tons of issues and violations of "best practices" here, but it gets the job done for us without creating a ton of custom converter classes.
If you don't use code-generated classes, you can decorate your properties with the ScriptIgnoreAttribute to tell the serializer to ignore certain properties. Xml serialization has a similar attribute.
Of course, you cannot use this approach if you want to return some properties of a class on one service method call and different properties of the same class on a different service method call. If you want to do that, return an anonymous type in the service method.
[WebMethod]
[ScriptMethod]
public object GimmieData()
{
var dalEntity = dal.GimmieEntity(); //However yours works...
return new
{
id = dalEntity.Id,
description = dalEntity.Desc
};
}
The serializer could care less about the type of the object you send to it, since it just turns it into text anyway.
I also believe that you could implement ISerializable on your data entity (as a partial class if you have code-gen'd data entities) to gain fine-grained control over the serialization process, but I haven't tried it.
I know this thread has been quiet for a while, but I thought I'd offer that if you override the SupportedTypes property of JavaScriptConverter in you custom converter, you can add the types that should use the converter. This could go into a config file if necessary. That way you wouldn't need a custom converter for each class.
I tried to create a generic converter but couldn't figure out how to identify it in the web.config. Would love to find out if anyone else has managed it.
I got the idea when trying to solve the above issue and stumbled on Nick Berardi's "Creating a more accurate JSON .NET Serializer" (google it).
Worked for me:)
Thanks to all.
If you're using .NET 3.x (or can), a WCF service is going to be your best bet.
You can selectively control which properties are serialized to the client with the [DataMember] attribute. WCF also allows more fine-grained control over the JSON serialization and deserialization, if you desire it.
This is a good example to get started: http://blogs.msdn.com/kaevans/archive/2007/09/04/using-wcf-json-linq-and-ajax-passing-complex-types-to-wcf-services-with-json-encoding.aspx
You can use the System.Runtime.Serialization.Json.DataContractJsonSerializer class in the System.ServiceModel.Web.dll assembly.
Don't quote me on this working for certain, but I believe this is what you are looking for.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public XmlDocument GetXmlDocument()
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(_xmlString);
return xmlDoc;
}