I have a json which i map to an object as follows:
original json:
{
"amount_predicted" : 1.34
}
mapped:
public class Amount {
#JsonProperty("amount_predicted")
private BigDecimal amountPredicted;
}
I do some work and check this amount and then send this to the client. The issue i have is i want the name of the field to be camelcase on output. But since i already use the json property it wont do that.
This is what id like:
{
"amountPredicted" : 1.34
}
You can annotate the getter and the setter of the amountPredicted field in the Amount class respectively with #JsonProperty("amount_predicted") and #JsonProperty("amountPredicted") annotations thus differentiating the property's name in the deserialization process from the one used for the serialization:
public class Amount {
private BigDecimal amountPredicted;
#JsonProperty("amount_predicted")
public void setAmountPredicted(BigDecimal amountPredicted) {
this.amountPredicted = amountPredicted;
}
#JsonProperty("amountPredicted")
public BigDecimal getAmountPredicted() {
return amountPredicted;
}
}
Amount amount = mapper.readValue(json, Amount.class);
//it will print {"amountPredicted":1.34}
System.out.println(mapper.writeValueAsString(amount));
EDIT: as pointed out by #user1555190 is much simpler to use the JsonAlias annotation:
public class Amount {
#JsonAlias(value = {"amount_predicted"})
private BigDecimal amountPredicted;
}
Related
I want to test a contract where one field is of type java.time.Instant. But not all instances of an Instant are handled as I expect by spring-cloud-contract. Given the following simple contract:
Contract.make {
description("Get a version")
request {
method 'GET'
url '/config/version'
headers {
contentType(applicationJson())
}
}
response {
status 200
body(
nr: 42,
creationDate: producer(anyIso8601WithOffset())
)
headers {
contentType(applicationJson())
}
}
}
And this service implementation:
#RestController
public class VersionController {
#GetMapping(path = "/version")
public ResponseEntity<Version> getCurrentVersion() {
return ResponseEntity.ok(new Version(42, Instant.ofEpochMilli(0)));
}
}
Executing gradle test works fine. But if I replace the Instant with Instant.now(), my provider test fails with
java.lang.IllegalStateException: Parsed JSON [{"nr":42,"creationDate":"2018-11-11T15:28:26.958284Z"}] doesn't match the JSON path [$[?(#.['creationDate'] =~ /([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.\d{3})?(Z|[+-][01]\d:[0-5]\d)/)]]
which is understandable because Instant.now() produces an Instant whose string representation does indeed not match the anyIso8601WithOffset() pattern. But why is this? Why are Instants represented differently and how can I describe a contract that validates for any instant?
Ok, I found a solution that works for me. Although I do not know if this is the way to go.
In order to always get the exact same format of the serialized instant, I define the format of the corresponding property of my version bean as follows:
public class Version {
private final int nr;
private final Instant creationDate;
#JsonCreator
public Version(
#JsonProperty("nr") int nr,
#JsonProperty("creationDate") Instant creationDate)
{
this.nr = nr;
this.creationDate = creationDate;
}
public int getNr() {
return nr;
}
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", timezone = "UTC")
public Instant getCreationDate() {
return creationDate;
}
}
I'm using
Spring 3.1.0.RELEASE
Jackson 1.9.5
I'm using org.springframework.web.client.RestTemplate's getForObject() method:
getForObject(String url, Class<?> responseType, Map<String, ?> urlVariables) throws RestClientException
Here's my JSON:
{
"someObject": {
"someKey": 42,
},
"key2": "valueA"
}
Here's the POJO used to hold it:
SomeClass.java:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"someObject",
"key2"
})
public class SomeClass {
#JsonProperty("someObject")
private SomeObject someObject;
#JsonProperty("key2")
private String key2;
#JsonProperty("someObject")
public LocationInfo getSomeObject() {
return someObject;
}
#JsonProperty("someObject")
public void setLocationInfo(SomeObject someObject) {
this.someObject = someObject;
}
}
SomeObject.java:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"someKey"
})
public class SomeObject{
#JsonProperty("someKey")
private String someKey;
#JsonProperty("someKey")
public String getSomeKey() {
if(someKey==null){
someKey = "";
}
return someKey.toUpperCase();
}
#JsonProperty("someKey")
public void setSomeKey(String someKey) {
this.someKey = someKey;
}
}
It works. Given the JSON structure, I get a String value of "42" in the property someKey of class SomeObject
I don't understand why. Is there some magical conversion going on behind the scenes of which I'm unaware?
Can the conversion be counted on? Also, i'm not currently getting any whitespace at the beginning or end of the String someKey. Is that something I can count on as well, since the integer value cannot have any whitespace?
Check out the code at https://github.com/joelittlejohn/jsonschema2pojo if you want to really understand how it works.
Yes the conversion can be counted on, yes you can count on their not being whitespace in the String in the pojo.
In a nutshell the fields from the JSON file are read in, then these get mapped to the member variables/setter methods of the Pojos that is passed in as your responseType.
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...
}
}
I am using JAX-RS (CXF) with JaxB and Jackson to provide a REST-API.
Unfortunately, none of the found results helps me with following (simple) problem:
I implemented following method:
#POST
#Path(ApiStatics.ARMY_CREATE_ARMY)
public com.empires.web.dto.Army createArmy(#FormParam("locationid") long locationId, #FormParam("name") String name, #FormParam("troops") ArmyTroops troops) {
and here are is my model class:
#XmlRootElement
#XmlSeeAlso(ArmyTroop.class)
public class ArmyTroops {
public ArmyTroops() {
}
public ArmyTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
#XmlElement(name = "troops")
private List<ArmyTroop> troops = new ArrayList<ArmyTroop>();
public List<ArmyTroop> getTroops() {
return troops;
}
public void setTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
}
ArmyTroop
#XmlRootElement(name = "troops")
public class ArmyTroop {
#XmlElement
private long troopId;
#XmlElement
private String amount;
public long getTroopId() {
return troopId;
}
public void setTroopId(long troopId) {
this.troopId = troopId;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
}
My json that i send looks like this:
locationid 1
name asdasd
troops {"troops":[{"troopId":4,"amount":"5"},{"troopId":6,"amount":"5"}]}
Unfortunately, the object gets not transformed. Instead I receive this error:
InjectionUtils #reportServerError - Parameter Class com.empires.web.dto.in.ArmyTroops has no constructor with single String parameter, static valueOf(String) or fromString(String) methods
If I provide the constructor with a single string parameter, I get passed the whole json string for "troops" as mentioned above.
Any ideas why JaxB does not work at this point?
You are passing all your parameters with #Form annotation.
But the Form part of the http message must be an xml data structure.
Your 3 parameters don't have a main xml datastructure so it wont work.
In short, form params are send as body.
Cxf use the MultivaluedMap to send params (cxf have an xml model for this structure).
As you can see it is not fit for parameters that can't be trivally serialized.
Here me solution would be to drop the #FormParam to avoid the problem:
1) Use #PathParam #CookieParam to send yours first 2 parameters, and the 'no tag' (body) only for the army compositions.
2) Define an uber object that take all parameters and can be serialized as xml datastructure and use the 'no tag' (body) sending.
3) Use soap, with cxf it is really easy to gets both Rest and Soap.
I have structure IntEx - in short it extends normal Int32 and processed operation. It looks like this:
[Serializable]
public struct IntEx
{
private int internalValue;
private IntEx(int value)
{
internalValue = value;
}
public static implicit operator int(IntEx value)
{
return value.internalValue;
}
public static implicit operator IntEx(int value)
{
return new IntEx(value);
}
}
If we send this structure through WCF it serialize using JSON and output will "nicely look". Like we will use sample code below:
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(TestClass));
using (MemoryStream stream = new MemoryStream())
{
jsonSerializer.WriteObject(stream, testClass);
string serializedString = Encoding.UTF8.GetString(stream.GetBuffer());
Console.WriteLine("JSON: {0}", serializedString);
}
public class TestClass
{
public int I1 { get; set; }
public IntEx I2 { get; set; }
}
Output look like this
JSON: {"I1":11,"I2":{"internalValue":22}}
Client and other "third-part" progam use this format (with internalValue).
Using IntEx is widely use in my application. One of the object is serialized to XML (some kind of setting). This object use IntEx as type.
So I have to implement IXmlSerializable to structure IntEx, because without this property is serialized like empty node
XML: <TestClass><I1>11</I1><I2 /></TestClass>
If I change IntEx to use IXmlSerializable
[Serializable]
public struct IntEx : IXmlSerializable
{
private int internalValue;
private IntEx(int value)
{
internalValue = value;
}
public static implicit operator int(IntEx value)
{
return value.internalValue;
}
public static implicit operator IntEx(int value)
{
return new IntEx(value);
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
throw new NotImplementedException();
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteValue(internalValue);
}
}
XML output look ok
XML: <TestClass><I1>11</I1><I2>22</I2></TestClass>
but all my service break down, since now JSON look like this:
JSON: {"I1":11,"I2":"<IntEx xmlns=\"http:\/\/schemas.datacontract.org\/2004\/07\/TestJSONSerialization\">22<\/IntEx>"}
I read that if you use IXmlSerializable, JSON serialization "think" that I'm responsible for serialize so leave this object to me... But how can I change back to "original" serialization.
So now I'm in deadend... I need JSON output look like before, but I also need to some how force to write setting to XML with two conditions:
internalValue should remain private - it shouldn't be accessible using some public Property
I don't want rewrite bunch of code to chaneg (use boxing for JSON's properties) or change all possible property or class that can be saved to XML file.
So can anyone give me some clue, how I can resolve this issue? :/
you can use DataContractJsonSerializer with IDataContractSurrogate. using the IDataContractSurrogate to convert "IntEx" to "IntExJson", and the "IntExJson" don't need to inherit from IXmlSerializable.
IDataContractSurrogate can be used to remove some features from object, and convert to the similar object. and then use:
public DataContractJsonSerializer(Type type, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, IDataContractSurrogate dataContractSurrogate, bool alwaysEmitTypeInformation);
to serialize the object to json. the deserialization is same.