In Salesforce/Apex how can i serialize an Apex Class to Json/String with a change of variable names mapping ? like in java we can use #SerializedName annotation.
So far, Apex does not support annotation for serialization. The supported annotations are
here
But, in this type of scenario, I always follows
public class TestClass
{
public string oldA { get; set; }
public string oldB { get; set; }
public string oldC { get; set; }
}
String jsonStr = JSON.serialize(objectTestClass);
jsonStr = jsonStr.replaceAll('"oldA":','"newA":');
You can use JSONGenerator to create the output yourself
There's no out of the box way for remapping variables via an annotation or a similar facility. To remap a variable you'd have to roll your own parser via JSONParser or use untyped deserialization and encapsulate it into your own Apex class.
Related
I have this API
public ActionResult AddDocument([FromBody]AddDocumentRequestModel documentRequestModel)
{
AddDocumentStatus documentState = _documentService.AddDocument(documentRequestModel, DocumentType.OutgoingPosShipment);
if (documentState.IsSuccess)
return Ok();
return BadRequest();
}
And this is my request model
public class AddDocumentRequestModel
{
public AddDocumentRequestModel(int partnerId, List<ProductRequestModel> products)
{
PartnerId = partnerId;
Products = products;
}
[Range(1, int.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int PartnerId { get; private set; }
[Required, MustHaveOneElement(ErrorMessage = "At least one product is required")]
public List<ProductRequestModel> Products { get; private set; }
}
so when I'm trying to hit the API with this body
{
"partnerId": 101,
"products": [{
"productId": 100,
"unitOfMeasureId": 102,
"quantity":5
}
]
}
this is the request : System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'Alati.Commerce.Sync.Api.Controllers.AddDocumentRequestModel'
I don't need parameterless constructor,because it doesn't read the body parameters.Is there any other way for deserialization?
You can achieve your desired result. You need to switch to NewtonsoftJson serialization (from package Microsoft.AspNetCore.Mvc.NewtonsoftJson)
Call this in Startup.cs in the ConfigureServices method:
services.AddControllers().AddNewtonsoftJson();
After this, your constructor will be called by deserialization.
Extra info: I am using ASP Net Core 3.1
Later Edit: I wanted to give more info on this, as it seems that this can also be achieved by using System.Text.Json, although custom implementation is necessary. The answer from jawa states that Deserializing to immutable classes and structs can be achieved with System.Text.Json, by creating a custom converter (inherit from JsonConverter) and registering it to the converters collection (JsonSerializerOptions.Converters) like so:
public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
...
}
and then...
var serializeOptions = new JsonSerializerOptions();
serializeOptions.Converters.Add(new ImmutablePointConverter());
serializeOptions.WriteIndented = true;
Just in case someone have the same issue I had, I was using abstract class, once removed the abstract key word, it all worked just fine.
Just Add [JsonConstructor] before your constructor
like this
public class Person
{
public string Name { get; set; }
public int LuckyNumber { get; private set; }
[JsonConstructor]
public Person(int luckyNumber)
{
LuckyNumber = luckyNumber;
}
public Person() { }
}
There are still some limitations using System.Text.Json - have a look here: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson
Deserialization without parameterless constructor using a parameterized constructor is not supported yet (but it's on their plan). You can implement your custom JsonConverter (like in this example: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize-to-immutable-classes-and-structs) or - like Adrian Nasul above suggested: use Newtonsoft.Json and then you can use the [JsonConstructor] attribute
In my case I had set a class as internal and when I made it public it worked. The error message was really of little help with this specific circumstance.
Old (actual class name changed to ClassName in the example
internal class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = className?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
New:
public class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = branding ?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
In my case error, caused was inside InnerException. There is my class had a field with a custom class type that did not have a parameterless constructor. I've added a parameterless constructor to the inner class and the problem has gone away.
This is one of the classes in Interface file.
[DataContract]
public class ClassX
{
public ClassX()
{
ClassXParameters = new List<ClassXParameter>();
}
public void Add(string name, string value)
{
ClassXParameters.Add(new ClassXParameter() { Name = name, Value = value });
}
[DataMember]
public List<ClassXParameter> ClassXParameters { get; set; }
}
[DataContract]
public class ClassXParameter
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
}
on the client I'm trying to do something like this
ClassX classx = new ClassX();
classx.Add("testname", "testvalue");
But this .Add method is not even visible.
currently I'm doing
ClassX classx = new ClassX();
List<ClassXParameter> params = new List<ClassXParameter()>;
params.add(new ClassXParameter() {Name="testname", Value="testvalue"});
classx.ClassXParameters = params;
Is there anyway I can do what I'm trying to do?
Note: I am not sure why some of the text above are in bold.
If you autogenerate the client code from scratch, it will generate a new class, which contains those members and properties that are marked with DataContract.
If you have methods that you want available on the client, you can accomplish this by putting the DataContract types in an own assembly, which you reference from both the server and the client. When you generate the service reference you have to choose the option to reuse existing classes instead of generating new ones.
Often it is suitable to put data validation rules in the data contract classes property setters. Reusing the data contract assembly in the client will cause the data validation to occur directly on the client, without the need for a roundtrip. It also causes the error in a place where it is much easier to spot than if it is reported as deserialization error.
Data Contracts are for data only. Any methods will not be visible on the client.
The bold was because of the "-----".
What's the practical way of serializing an instance of subclass by using DataContractSerializer?
For example, here are the data types:
[DataContract]
public class Car
{
public Car()
{
Wheels = new Collection<Wheel>();
}
[DataMember]
public Collection<Wheel> Wheels { get; set; }
}
[DataContract]
public abstract class Wheel
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class MichelinWheel : Wheel
{
[DataMember]
public string Wheel1Test { get; set; }
}
[DataContract]
public class BridgeStoneWheel : Wheel
{
[DataMember]
public string Wheel2Test { get; set; }
}
Then here is the code that creates a car with two differen wheels:
Car car = new Car();
MichelinWheel w1 = new MichelinWheel { Name = "o1", Wheel1Test = "o1 test" };
BridgeStoneWheel w2 = new BridgeStoneWheel { Name = "o2", Wheel2Test = "o2 test" };
car.Wheels.Add(w1);
car.Wheels.Add(w2);
Now if I try to serialize the car by using DataContractSerializer, I will get an exception that says MichelinWheel is not expected. And I have to modify the Wheel class like this to make it work:
[DataContract]
[KnownType(typeof(MichelinWheel))]
[KnownType(typeof(BridgeStoneWheel))]
public abstract class Wheel
{
[DataMember]
public string Name { get; set; }
}
But this approach is not practical, because I am not able to list all kinds of wheels before they are created. And changing the Wheel class every time after a new brand of wheel is created is also not practical, because they might by created in third-party code.
So, what is the practical approach of serializing an instance of a subclass when using DataContractSerializer?
Thanks
Check this article using DataContractResolver from WCF 4. You can also use KnownTypeAttribute with passing name of a method that will use reflection to get all types. Anyway service requires that all types are known before it starts.
There are several ways to make known types available to the service.
The simplest you have outlined above, but obviously this requires you to recompile when new types are added, and depending on your configuration can make it awkward to avoid circular dependencies.
You can also configure the KnownTypes:
through the service configuration file (service restart only required),
add them as service known types provided through a static method on the service interface which you could get through reflection as Ladislav Mrnka has indicated (you could probably reflect over all loaded assemblies and return all types that have the DataContact attribute on them as known types, but I couldn't find an example of that.)
implement your own way of getting them (perhaps through some bespoke configuration elements in the config file or just through a text file)
[DataMember]
public int? NumberOfPages; //////////// Is this supported????
[DataMember]
public bool? Color; //////////// Is this supported????
[DataMember]
public int? BulkQuantity;
[DataMember]
Yes, of course!
You should have no trouble whatsoever to create nullable data members, they'll be handled in the resulting WSDL/XSD as "xs:nillable=true" members. No problem at all.
Yes, please see Types Supported by the Data Contract Serializer:
Nullable types are fully supported by the data contract serializer.
#Kahoon and Batwad:
We solved this problem by using the nullable<> or ? type in two steps:
In the class containing the generic field, define the field as follows:
nullable<GenType> MyField {get; set;}
In the data contract that uses this baseclass, you can define which elements are known to the serializer/deserializer using some annotation-like tags. Here, we defined for example:
[Serializable]
[DataContract]
[KnownType(typeof(BaseClass<nullable<DateTime>>))]
Instead of BaseClass<nullable<DateTime>> you can use BaseClass<DateTime?>, I think.
After this, the serialization of generic null values worked for us.
In my case It looks like that the Nullable Integer passed in is treated as Empty String and NOT Null Value
So here is how I handle the nullable in the code
[XmlIgnore]
public int? NumberOfPagesCount{ get; set; }
[XmlElement("NumberOfPages")]
public string NumberOfPagesText
{
get { return this.NumberOfPagesCount.HasValue ? this.NumberOfPagesCount.Value.ToString("F2") : string.Empty; }
set
{
if (!string.IsNullOrEmpty(value))
{
this.NumberOfPagesCount= Convert.ToInt32(value);
}
else
{
this.NumberOfPagesCount= null;
}
}
}
I'm trying to save a mapped entity using NHibernate but my insert to the database fails because the underlying table has a column that does not allow nulls and IS NOT mapped in my domain object. The reason it isn't mapped is because the column in question supports a legacy application and has no relevance to my application - so I'd like to not pollute my entity with the legacy property.
I know I could use a private field inside my class - but this still feels nasty to me. I've read that I can use an NHibernate interceptor and override the OnSave() method to add in the new column right before my entity is saved. This is proving difficult since I can't work out how to add an instance of Nhibernate.type.IType to the types parameter of my interceptor's OnSave.
My Entity roughly looks like this:
public class Client
{
public virtual int Id { get; set; }
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual string Url { get; set; }
}
And my interceptor
public class ClientInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
if (entity is Client)
{
/*
manually add the COM_HOLD column to the Client entity
*/
List<string> pn_list = propertyNames.ToList();
pn_list.Add("COM_HOLD");
propertyNames = pn_list.ToArray();
List<Object> _state = state.ToList();
_state.Add(false);
state = _state.ToArray();
//somehow add an IType to types param ??
}
return base.OnSave(entity, id, state, propertyNames, types);
}
}
Does anyone have any ideas on how to do this properly?
I can't say for sure since I've never actually done this (like Stefan, I also prefer to just add a private property), but can you just add a NHibernate.Type.BooleanType to the types array?
List<IType> typeList = types.ToList();
typeList.Add(new BooleanType());
types = typesList.ToArray();
EDIT
Yes, it looks like you are right; the types have an internal constructor. I did some digging and found TypeFactory:
Applications should use static
methods and constants on
NHibernate.NHibernateUtil if the
default IType is good enough. For example, the TypeFactory should only
be used when the String needs to have a length of 300 instead of 255. At this point
NHibernate.String does not get you thecorrect IType. Instead use TypeFactory.GetString(300) and keep a
local variable that holds a reference to the IType.
So it looks like what you want is NHibernateUtil:
Provides access to the full range of
NHibernate built-in types. IType
instances may be used to bind values
to query parameters. Also a factory
for new Blobs and Clobs.
typeList.Add(NHibernateUtil.Boolean);
Personally I wouldn't do it so complicated. I would add the private property and assign it a default value - finished. You could also consider a default value in the database, then you don't need to do anything else.
private virtual bool COM_HOLD
{
get { return false; }
set { /* make NH happy */ }
}
Before writing a interceptor for that I would consider to write a database trigger. Because with the Interceptor you are "polluting" your data access layer. It could make it unstable and you could have strange problems.