Expression serialization/deserialization in C# - json

I try to realize database access decorator based on Expression types. So, I've already tried many different json serializing libraries, started from Newtonsoft Json till DataContractJsonSerializator and etc.
1) Most of serializators crash on Expression type serialization (including System.Text.Json.Serialization).
2) Newtonsoft.JsonSerializer successfully serialize Expression<Func<User, bool>> test = e => e.Id == sameUser.Id, where User is the class like:
public class User
{
public Guid Id { get; set; }
public string Fullname { get; set; }
}
and sameUser is an object of User class.
But Newtonsoft.JsonSerializer produces string of ~169-200 millions symbols. I don't know does Newtonsoft.JsonSerializer correctly deserialize this json of the other side becauseof the size. Ofcourse, I've tried to use different serializing options.
3) ServiceStack.Text.JsonSerializer successfully serialize Expression<Func<User, bool>> test = e => e.Id == sameUser.Id with normal json size (approx 2-3 thousands symbols), but on the deserialization Expression.Body always null after deserialization (and this really strange - serialized json has it well-serialized).
4) Serialize.Linq successfully passed the test.
I want to understand, what the reason of this strange behavior of main serializers like Newtonsoft, ServiceStack, Microsoft, etc?
P.S. I'ven't tested protobuf-net and MessagePack yet, I'll do this soon,but think they have the same troubles with Expression class object serialization/deserialization.

Expression has cyclical dependencies an non serializable references that is not suitable for serialization. If you want to serialize the debug string representation of an Expression do that in your code and serialize the string, don’t expect serialization libraries to attempt to serialize a non-serializable class that’s impossible to deserialize.
If you want to serialize code, send raw source code and use Roslyn or Code DOM to execute the source code received, you’ll need to validate any untrusted user code for potential security vulnerabilities or unwanted behavior before evaluating it.

Related

Serialize/deserialize a Dictionary with a comma-separated entry

I am developing a ASP.NET Core 3.1 website and I have data in a Dictionary<string, object> that I want to Serialize/Deserialize using Microsoft System.Text.Json (I am new to Json serialize/deserialize in fact). The data comes from a PostgreSQL DB query and one of the returned values is a comma-separated list of integers (converted to string) that results from the STRING_AGG function. The image below shows one of the entries of the Dictionary:
I serialize it using the following code. Please note that I have tried both Microsoft System.Text.Json and Newtonsoft.
jsonResult = Newtonsoft.Json.JsonConvert.SerializeObject(result);
//jsonResult = JsonSerializer.Serialize(result);
The data in the Dictionary should be deserialized according to the following class structure:
I use the following code:
//IEnumerable<SeccGralContenidoViewModel> seccGralContenido = JsonSerializer.Deserialize<IEnumerable<SeccGralContenidoViewModel>>(_seccGralContenidoRepository.Read());
IEnumerable<SeccGralContenidoViewModel> seccGralContenido = Newtonsoft.Json.JsonConvert.DeserializeObject <IEnumerable<SeccGralContenidoViewModel>>(_seccGralContenidoRepository.Read());
However, an exception is thrown when deserializing no matter if I use Newtonsoft or System.Text.Json:
I am originally using System.Text.Json namespace but I also tried using Newtonsoft. After analyzing a bit deeper, I see that the problem could be the way in which data is saved to the Dictionary but I have not found a workaround.
If you don't want to write a custom converter then the simplest solution is to introduce another property:
public string CategoriasContenidolds {get; set;}
private static char delimiter = ',';
[JsonIgnore]
public string[] CategoriasContenidolds_Collection
{
get => CategoriasContenidolds.Split(delimiter).Select(item => item.Trim()).ToArray();
set => CategoriasContenidolds = string.Join(delimiter, value);
}
The serializer will use the CategoriasContenidolds property during serialization and deserialization
You should use CategoriasContenidolds_Collection (or name whatever you want) in your business logic
By explicitly marking this property with JsonIgnore the serializer will ignore that
I could solve my issue by directly getting JSON formatted results from queries. PostgreSQL does an excellent job. This way I also avoid performing a 2-step process: first, getting the query result; second, serializing to JSON.

how to deserialize a json string that contains ## with scala'

As the title already explains, I would like to deserialize a json string that contains a key that starts with ##. With the ## my standard approach using case classes sadly does not work anymore.
val test = """{"##key": "value"}"""
case class Test(##key: String) // not possible
val gson = new GsonBuilder().create()
val res = gson.fromJson(test, classOf[Test])
How can work with the ## withtout preprocessing the input json string?
The simplest answer is to quote the field name:
case class Test(`##key`: String)
I experimented a bit but it seems that GSON doesn't interoperate well with Scala case classes (or the other way around, I guess it's a matter of perspective). I tried playing around with scala.beans.BeanProperty but it doesn't seem like it makes a difference.
A possible way to go is to use a regular class and the SerializedName annotation, as in this example:
import com.google.gson.{FieldNamingPolicy, GsonBuilder}
import com.google.gson.annotations.SerializedName
final class Test(k: String) {
#SerializedName("##key") val key = k
override def toString(): String = s"Test($key)"
}
val test = """{"##key": "foobar"}"""
val gson = new GsonBuilder().create()
val res = gson.fromJson(test, classOf[Test])
println(res)
You can play around with this code here on Scastie.
You can read more on SerializedName (as well as other naming-related GSON features) here on the user guide.
I'm not a Scala programmer, I just used javap and reflection to check what the Scala compiler generated and slightly "learnt" how some Scala internals work.
It does not work for you because of several reasons:
The Scala compiler puts case class elements annotations to the constructor parameters, whereas Gson #SerializedName can only work with fields and methods:
// does not work as expected
case class Test(#SerializedName("##key") `##key`: String)
From the plain Java perspective:
final Constructor<Test> constructor = Test.class.getDeclaredConstructor(String.class);
System.out.println(constructor);
System.out.println(Arrays.deepToString(constructor.getParameterAnnotations()));
public Test(java.lang.String)
[[#com.google.gson.annotations.SerializedName(alternate=[], value=##key)]]
Not sure why the Scala compiler does not replicate the annotations directly to the fields, but the Java language does not allow annotating parameters with the #SerializedName annotation causing a compilation error (JVM does not treats it as a failure either).
The field name is actually encoded in the class file.
From the Java perspective:
final Field field = Test.class.getDeclaredField("$at$atkey"); // the real name of the `##key` element
System.out.println(field);
System.out.println(Arrays.deepToString(field.getDeclaredAnnotations()));
private final java.lang.String Test.$at$atkey <- this is how the field can be accessed from Java
[] <- no annotations by default
Scala allows moving annotations to fields and this would make your code work accordingly to how Gson #SerializedName is designed (of course, no Scala in mind):
import scala.annotation.meta.field
...
case class Test(#(SerializedName#field)("##key") `##key`: String)
Test(value)
If for some/any reason you must use Gson and can't annotate each field with #SerializedName, then you can implement a custom type adapter, but I'm afraid that you have to have deep knowledge in how Scala works.
If I understand what Scala does, it annotates every generated class with the #ScalaSignature annotation.
The annotation provides the bytes() method that returns a payload that's most likely can be used to detect whether the annotated type is a case class, and probably how its members are declared.
I didn't find such a parser/decoder, but if you find one, you can do the following in Gson:
register a type adapter factory that checks whether it can handle it (basically, analyzing the #ScalaSignature annotation, I believe);
if it can, then create a type adapter that is aware of all case class fields, their names possibly handling the #SerializedName yourself, as you can neither extend Gson ReflectiveTypeAdapterFactory nor inject a name remapping strategy;
take transient fields (for good) and other exclusion strategies (for completeness) into account;
read/write each non-excluded field.
Too much work, right? So I see two easy options here: either use a Scala-aware JSON tool like other people are suggesting, or annotate each field that have such a special name.

Java validator for String

How do I validate using Hibernate validator for elements appearing more than once in a JSON payload bound to a Java class annotated with validator annotations?
Let's say I have the following:
class Person {
String name;
int age;
}
I am binding JSON to Person.
The JSON payload looks like the following:
{
"name":"someName",
"age":30
}
Let's say the payload has 2 "name" fields repeated as below.
{
"name":"someName",
"name" : "otherName",
"age":30
}
Then I like to use the validator to validate this. It will work for Collection objects if I use #Size(min=1, max=1).
I am wondering how I make this work for String. With String #Size tries to look for the length of the string content and not the number of times the string content in the payload.
Thanks for your time!
This is not possible. JSON deserialization and Bean validation are two entirely different things. By the time your Hibernate validation kicks in all it sees is a Person object, with a single name field.
It is the behavior of your JSON library that will determine which of the "name" fields will be deserialized into the Java bean (or if an exception will be thrown). For the most part, if you want to validate that no duplicates are supplied then you are going to need to write some custom deserialization code.

WinRT serializing a DateTimeOffset

I'm working on a Windows 8 Metro application that references a c# WinRT project. Among other things, the c# project makes web requests to an Azure service to perform CRUD operations against a SQL Azure database.
When performing a POST operation on the service, I'm serializing an instance of a class and putting it in the body of the request.
public sealed class Foo
{
int FooId { get; set; }
DateTimeOffset FooDate { get; set; }
}
When this is serialized using the DataContractJSONSerializer, the result is something like this:
{"FooId":1,"FooDate":{"DateTime":"/Date(1342732970000)/","OffsetMinutes":-420}}
FYI that this is 7/19/2012 2:22:50PM -07:00.
OK great ... Only problem is that the Azure service is expecting just a DateTime, not a DateTimeOffset. I don't own the Azure service so I can't change its behavior.
So (ignoring that I'm losing the offset) what I need is this to serialize into:
{"FooId":1,"FooDate":"/Date(1342732970)/"}
My first approach was to add a new aliased DateTime property/datamember to the class with a getter that returns the DateTime portion of the DateTimeOffset. However, WinRT doesn't support the DateTime type.
There are a couple of hacky ways to get around this, but I wanted to see if there's an elegant way to do this before resorting to one of these:
Regex on the serialization result before the POST
String property on the class that returns a JSON formatted date
Thanks
I ended up implementing a property on the class with a getter that formats the date appropriately.
I decorated the Foo field with the IgnoreDataMember attribute so that it gets ignored during serialization. I then added a new field and gave it the alias of Foo for serialization.
Thanks

How to serialize transient fields in the model via FlexJson?

I am using Play Framework to expose REST API, which returns some JSON objects.
To simplify the API usage, I would like to return a "calculated" field in the response.
Unfortunately, in my tests, while FlexJson does not ignore the transient model fields completely, but always sets them to 'null'.
More details:
In the model class, I define:
#Transient
public String currencyName;
The only constructor of the class set the value to "dollar" (for debugging purposes):
this.currencyName = "dollar";
When serializing the class using FlexJson, when the 'currencyName' field is not specified in the include/ exclude - the result always looks like:
"currencyName":null
Any idea what got wrong, and how to get the field value serialized into JSON?
Thanks in advance.
By definition if your field is transient it will not be serialized. Perhaps this field should not be transient in your application if the state matters.