Multiple Any-Setters using jackson? - json

I have a dynamic field, where the key is unknown and the value can be either a String or an Object.
Also, the field itself can be a map or just a single value.
How can I instruct Jackson to serialize/deserialize this field according to it's value type?
I'd also like to save my typed object to use in my code and not just use a map of Map<String, Object>.
public class MyPackage {
#JsonProperty("versions")
public Versions versions = new Versions();
#JsonAnySetter
public void add(String key, MyVersionObject value) {
versions.put(key, value);
}
#JsonAnyGetter
public Map<String, MyVersionObject> getMap() {
return versions;
}
}
Different JSONs that this field should accept:
{
"versions": {
"1.0": { ... MyVersionObject fields},
"2.0": { ... MyVersionObject fields},
"3.0": { ... MyVersionObject fields},
}
}
Or
{
"versions": {
"3.0": "latest"
}
}

Sounds like input is so irregular that you can't really define this dynamically. So leave type of value as Object; this will give you String or Map. And from that, convert yourself; either manually, or by using ObjectMapper.convertValue() method.

Related

Json property alias in typescript

Let's say I want to get a data from Visual Studio TFS and the response (as json) is in this kind of format:
{
"Microsoft.VSTS.Scheduling.StoryPoints": 3.0,
// ......
}
There's dot in the property name. Reading from other questions I found out that I can read that json in typescript by using an interface like this
export interface IStory { // I don't think this kind of interface do me any help
"Microsoft.VSTS.Scheduling.StoryPoints": number
}
And then I can use the property with this syntax:
var story = GetStoryFromTFS();
console.log(story["Microsoft.VSTS.Scheduling.StoryPoints"]);
But I'd prefer not to call the property like this, since the intellisense won't able to help me finding which property I want to use (because I call the property using a string).
In C# there is a JsonProperty attribute which enable me to create a model like this:
public class Story
{
[JsonProperty(PropertyName = "Microsoft.VSTS.Scheduling.StoryPoints")]
public double StoryPoints { get; set; }
}
And then I can use the property this way:
var story = GetStoryFromTFS();
Console.WriteLine(story.StoryPoints);
This way the intellisense will able to help me finding which property I want to use.
Is there something like JsonProperty attribute in typescript? Or is there any other, better way, to achieve this in typescript?
You have many options. Just keep in mind that all of these options require you to pass the original data to the class that will access it.
Map the values.
class StoryMap {
constructor(data: IStory) {
this.StoryPoints = data["Microsoft.VSTS.Scheduling.StoryPoints"];
}
StoryPoints: number;
}
Wrap the data.
class StoryWrap {
constructor(private data: IStory) {}
get StoryPoints(): number { return this.data["Microsoft.VSTS.Scheduling.StoryPoints"] };
}
Build a decorator to map the data.
function JsonProperty(name: string) {
return function DoJsonProperty(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.get = function () {
return this.data[name];
}
descriptor.set = function (value) {
this.data[name] = value;
}
}
}
class StoryDecorator
{
constructor(private data: IStory) {}
#JsonProperty("Microsoft.VSTS.Scheduling.StoryPoints")
get StoryPoints(): number { return 0 };
}

#JsonProperty to be used only for deserialization from JSON, not for serilization to JSON

I have the following POJO. It contains data about my airport.
class MyClass
{
#JsonProperty("AirportCode")
String airportCode;
#JsonProperty("AirportID")
Integer airportId;
}
The POJO objects are created using a JSON received from an API. the API sample output is like below.
[
{
"AirportCode": "BBA",
"AirportID": 4276802,
},
{
"AirportCode": "SCQ",
"AirportID": 5325651,
}
]
My code is to function as follows
void func()
{
//Get JSON from API and convert to POJO
//Do some processing on the POJO
//Convert POJO into JSON and write to file
}
The file contents are as follows
[
{
"AirportCode": "BBA-IN",
"AirportID": 4276802,
},
{
"AirportCode": "SCQ-USA",
"AirportID": 5325651,
}
]
I however require the output to be in camel case (like the POJO)
[
{
"airportCode": "BBA-IN",
"airportId": 4276802,
},
{
"airportCode": "SCQ-USA",
"airportId": 5325651,
}
]
Is there anyway I can get #JsonProperty to be honored only during deserialisation to POJO and not during serialization to JSON?
Have you tried to annotate getters and setters with different jsonProperty values? Directly it won't work but if you change the getter/setter name it should as both methods will be processed as if they belonged to different fields.
Something like this:
class MyClass {
String airportCode;
Integer airportId;
#JsonProperty("airportCode")
public String getAirportCodeForJson() {
return airportCode;
}
#JsonProperty("AirportCode")
public void setAirportCode(String airportCode) {
this.airportCode = airportCode;
}
#JsonProperty("airportID")
public Integer getAirportIdForJson() {
return airportId;
}
#JsonProperty("AirportID")
public void setAirportId(Integer airportId) {
this.airportId = airportId;
}
}
You could deserialize into a Dictionary and then camel-case all keys. Afterwards you'd be able to use it as intended.

Change the json DateTime serialization in WCF 4.0 REST Service

I need to replace the DateTime serialization for JSON in WCF REST Self Hosted service. Right now, I'm using something like the following code to do it, but it's definitely not the way to go since it requires manipulating each class.
[DataContract]
public class Test
{
[IgnoreDataMember]
public DateTime StartDate;
[DataMember(Name = "StartDate")]
public string StartDateStr
{
get { return DateUtil.DateToStr(StartDate); }
set { StartDate = DateTime.Parse(value); }
}
}
where my utility function DateUtil.DateToStr does all the formatting work.
Is there any easy way to do it without having to touch the attributes on my classes which have the DataContract attribute? Ideally, there would be no attributes, but a couple of lines of code in my configuration to replace the serializer with one where I've overridden DateTime serialization.
Everything that I've found looks like I have to replace huge pieces of the pipeline.
This article doesn't appear to apply because in I'm using WebServiceHost not HttpServiceHost, which not part of the 4.5.1 Framework.
JSON.NET Serializer for WCF REST Services
By default WCF uses DataContractJsonSerializer to serialize data into JSON. Unfortunatelly date from this serializer is in very difficult format to parse by human brain.
"DateTime": "\/Date(1535481994306+0200)\/"
To override this behavior we need to write custom IDispatchMessageFormatter. This class will receive all data which should be returned to requester and change it according to our needs.
To make it happen to the operations in the endpoint add custom formatter - ClientJsonDateFormatter:
ServiceHost host=new ServiceHost(typeof(CustomService));
host.AddServiceEndpoint(typeof(ICustomContract), new WebHttpBinding(), Consts.WebHttpAddress);
foreach (var endpoint in host.Description.Endpoints)
{
if (endpoint.Address.Uri.Scheme.StartsWith("http"))
{
foreach (var operation in endpoint.Contract.Operations)
{
operation.OperationBehaviors.Add(new ClientJsonDateFormatter());
}
endpoint.Behaviors.Add(new WebHttpBehavior());
}
}
ClientJsonDateFormatter is simple class which just applies formatter ClientJsonDateFormatter
public class ClientJsonDateFormatter : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new ResponseJsonFormatter(operationDescription);
}
public void Validate(OperationDescription operationDescription) { }
}
In the formatter we took imput and serialize it with the changed Serializer:
public class ResponseJsonFormatter : IDispatchMessageFormatter
{
OperationDescription Operation;
public ResponseJsonFormatter(OperationDescription operation)
{
this.Operation = operation;
}
public void DeserializeRequest(Message message, object[] parameters)
{
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
string json=Newtonsoft.Json.JsonConvert.SerializeObject(result);
byte[] bytes = Encoding.UTF8.GetBytes(json);
Message replyMessage = Message.CreateMessage(messageVersion, Operation.Messages[1].Action, new RawDataWriter(bytes));
replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return replyMessage;
}
}
And to send information to client we need data writer - RawDataWriter. Its implementation is simple:
class RawDataWriter : BodyWriter
{
byte[] data;
public RawDataWriter(byte[] data)
: base(true)
{
this.data = data;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Binary");
writer.WriteBase64(data, 0, data.Length);
writer.WriteEndElement();
}
}
Applying all code will result in returning date in more friendly format:
"DateTime":"2018-08-28T20:56:48.6411976+02:00"
To show it in practice I created example in the github branch DateTimeFormatter.
Please check also this answer as very likely you also will need it.
There is a limitation in JSON to convert DateTime, specially according to your case.
Please see http://msdn.microsoft.com/en-us/library/bb412170(v=vs.110).aspx
and read the section Dates/Times and JSON
To resolve this problem, I simply changed the type of serialization from JSON to XML for all the calls including DateTime.
After long time discussion ,I have find out the solution for it.
Please Use the following Code to Solve serialized date..
[IgnoreDataMember]
public DateTime? PerformanceDate { get; set; }
[DataMember(EmitDefaultValue = false, Name = "PerformanceDate")]
public string UpdateStartDateStr
{
get
{
if (this.PerformanceDate.HasValue)
return this.PerformanceDate.Value.ToUniversalTime().ToString("s", CultureInfo.InvariantCulture);
else
return null;
}
set
{
// should implement this...
}
}

Including Class name in POJO - JSON Marshalling

Following this example.
GET response is:
{
"singer":"Metallica",
"title":"Enter Sandman"
}
If more objects were included output should be like this:
[{
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]
I want to get the 'classname' written too. Something like this:
{"Track":[ {
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]}
Any simple ways to achieve this?
Looking forward to get data directly into Datatables from a JAX-RS Resteasy (Jackson) Server. Also trying to avoid DTO.
class TrackList
{
private List<Track> Track = new ArrayList<Track>();
// setter, getter
}
GET method
public TrackList getTrackInJSON() {
EDIT
GET method
public String getTrackInJSON() {
// ... create list of objects
return convertToString(objects);
}
utility method
static <T> String convertToString(List<T> list) throws IOException
{
final String json = new ObjectMapper().writeValueAsString(list);
return new StringBuilder()
.append("{\"")
.append(list.get(0).getClass().getSimpleName())
.append("\":")
.append(json)
.append("}")
.toString();
}

APEX JSON Deserialize

I have JSON string that has nested objects with dynamic names that vary each time. For an instance:
{
"Objects": {
"dynamicName1": {
"name": "test"
},
"dynamicName2": {
"name": "test"
}
}
}
I was wondering how can you deserialize this string in APEX using wrapper classes?
I tried this:
public class masterobj
{ public childobj Objects;
}
public class childobj
{ public el dynamicName1;
public el dynamicName2;
}
public class el
{ public string name;
}
String s = '{"Objects":{"dynamicName1":{"name":"test"},"dynamicName2":{"name":"test"}}}';
masterobj mo = (masterobj)JSON.deserialize(s, masterobj.class);
which works well when you have declared the dynamic variable names in the class for each nested object.
The problem and the question is how can I make this work using a dynamic variable in the wrapper class. Because the object names will vary and also the number of the objects, I can't hard-code the names as they are different each time.
Any ideas?
You won't be able to deserialize a structure like that with the data binding features of the json parser, you'll need to use the streaming json parser to read it.
Use a Map:
public class masterobj
{
Map<String, el> Objects;
}