I'm working on RSS Feed using XStream library and I have following model (simplified for example purpose)
public class Item {
private String title;
private String link;
private String guid; }
And it works fine, it generates the part of the XML I need. The problem though is that I need to add custom attribute for guid ("isPermaLink") and I can't find any solution for that.
So the XML I need would be:
<item>
<title>Test</title>
<link>www.example.com/abc</link>
<guid isPermaLink="false">www.example.com/abc</guid>
</item>
I know I can make attribute by using
#XStreamAsAttribute
So I tried to add another class for GUID
public class Guid {
private String value = "http://example.com/abc";
#XStreamAsAttribute
private boolean isPermaLink = true;
}
and point to that in my Item class but then the generated code contains GUID part like this:
<guid2 isPermaLink="true">
<value>http://example.com/abc</value>
</guid2>
And I can't have this element inside. The value needs to be inline.
The question is - how can I make the value inline OR how can I add attribute to String field
Sorry for self answering but after spending some time on this topic I finally found an answer. Probably that can help someone so I'm posting it here.
Basically I think there is no default way of doing that. I went through all the tutorials on XStream and also looked at every single annotation available from XStream.
Finally I did manage to do a workaround that works. Basically in order to add an attribute to String (for example) you can use custom Converter and add an attribute there.
So on my guid field I'm using XStreamConverter to choose my converter:
#XStreamConverter(value = GuidConverter.class)
private String guid;
And the converter itself:
public class GuidConverter implements Converter{
#Override
public boolean canConvert(Class type) {
return String.class.isAssignableFrom(type);
}
#Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
String value = (String) source;
writer.addAttribute("isPermaLink", "false");
writer.setValue(value);
}
#Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
//TODO if you need - I don't because I'm only marshalling in my scenario
return null;
}
}
This a shame that there is no "out of box" solution for that because if you had more String on which you wanted attribute or you wanted make these attributes values dynamic then... well the solution is not going to work for you.
In my case it worked because I always need same value (isPermaLink="false") but in most cases that's not going to be enough.
Perhaps someone has better idea to do that
Related
I have the following object and its value is set via a REST call as follows.
#Getter
#Setter
public class Benefit {
#JsonProperty("text")
private String headerText; // To note, I can't modify this headerText name
}
Data set from a rest call.
ResponseEntity<Benefit> response =
template.exchange(url, HttpMethod.POST, request, Benefit.class);
Benefit benefit = response.getBody();
The return value from the rest call is in following format which is why I annotated it as text.
{
"text" : "some text"
}
After this, using this response, I am passing it down as a value to the client that called me.
But when I send the information down, I don't want to name it as text.
I want to call it as description. Thus my response will be as follows:
{
"description" : "some text"
}
Queries/ Pointers
1. Is there a way to do this without me having to manually set it?
2. This headerText is in use for different REST call. In that scenario, I need to both
receive the value as text and also return as text. (Thus that has no issues).
3. Preferably any possible solutions, should not affect above point 2.
4. But is ok if it will affect. I will go with an entirely new Benefit2 Object to resolve this if there is a solution which affects point 2.
One possible way to do this is to set the value to another variable and pass that down as follows only for the particular rest call.
But finding it very cumbersome as follows.
Add a new field called description
#Getter
#Setter
public class Benefit {
#JsonProperty("text")
private String headerText;
// add a new field
private String description;
}
After the rest call, do the following:
Benefit benefit = response.getBody();
benefit.setDescription(benefit.getHeadlineText);
benefit.setHeaderText(null);
Any better ways?
To clarify on the flow:
Client calls my service
My service calls another service and got back:
{
"text" : "some text"
}
I then return the following back to the client.
{
"description" : "some text"
}
Thoughts after discussion.
Intention to use this object in both places, when calling rest and when returning response to client.
#Getter
#Setter
public class TestBenefit extends Benefit {
#Getter(AccessLevel.NONE)
#JsonProperty("text")
private String text;
private String description;
public void setText(String text) {
this.description = text;
}
}
Over time I learned that trying to use one object for multiple purposes in these scenarios is more trouble than it is worth. You should create objects that cater to your requests and responses appropriately. Use base classes if necessary. Also, I wouldn't call it Benefit2. :o) Name your classes, to some degree, for what they are used for. You could do something like...
class BenefitForOtherPurpose extends Benefit {
#JsonProperty('description')
public String getHeaderText() {
return this.headerText;
}
}
To that end, I don't think there is a way using the Jackson API to adjust the #JsonProperty value dynamically short of some reflection kung-fu that, again, is likely more trouble than it is worth. And there's nothing I know of in the Jackson API to conditionally set that outside of this complex solution:
Conditional JsonProperty using Jackson with Spring Boot
I am creating a requestModel and let say a person doesn't send me some keys.
If that key is not present I want to put null if i get the value of the key.
I don't want to investigate if a key is present or not .
public class CustomerModel {
private Optional<String> s3Bucket;
private Optional<String> docType;
public String getS3Bucket() {
if(s3Bucket.isPresent()) {
return s3Bucket.get();
} else {
return null;
}
}
public void setS3Bucket(Optional<String> s3Bucket) {
this.s3Bucket = s3Bucket;
}
public Optional<String> getDocType() {
return docType;
}
public void setDocType(Optional<String> docType) {
this.docType = docType;
}
}
Do we have any library or something where.
1. If i get the key and it is not present in the coming request json, i will get the null out of it and if the key is present and has value . It will be stored as value.
2. When writing the getter for s3bucket (getS3Bucket), i dont want to write it for everykey value. Is there a automatic way to do this.
I looked at lot of posts but the scenario is not there.
P.S - I am new to java
I believe Jackson is exactly what you need. And if you are using Spring - it already uses Jackson under the hood I guess.
Here you can find some examples and documentation of how JSON mapping on to model class is done.
If you need to customize some behavior, you can use annotations like #JsonProperty (there are many).
If properties in your model class have the same names as properties in JSON, most probably you won't need to provide any further configs.
Here is a simple example:
public class User {
#JsonProperty("userName")
private String name;
private int age;
// getters and setters
}
And if you have JSON like this:
{
"userName" : "Foo Bar",
"age" : 18
}
Jackson will do all the magic for you unless you need something very specific.
If something is not in JSON you get (let's say you received JSON without age) - corresponding property in model class will be null if it is object type and default value (0, false, etc.) for primitives (in our case age would be 0).
First of all, let me thank you in advance for all of your help!
Let me explain my current problem:
I'm trying to wrap some legacy web service calls with a rest/json/swagger layer. The fact is those old services use a tricky propietary xml format, which looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<service_RegistroEventoSrv>
<inputMap type="map">
<mensajeMT type="map">
<identificador type="String">
<_value_>USUPRUEBA</_value_>
</identificador>
<modo type="String">
<_value_>1</_value_>
</modo>
</mensajeMT>
</inputMap>
</service_RegistroEventoSrv>
I've been able to serialize most of the objects and fields but I have a problem with the root tag. The main class used for the serialization is the following one, but I need that root tag takes its value from the "serviceId" property
public class CafRequestObject<I,O> {
private String serviceId;
private I inputMap;
private O outputMap;
private RequestMetaInfo requestMetaInfo;
private ResponseMetaInfo responseMetaInfo;
... getters and setters
}
In order to achieve that tricky and custom serialization, I have added some serializares to the jackson XmlMapper for spring rest template.
CafRequestObjectSerializer
public class CafRequestObjectSerializer extends CafXsdSerializer<CafRequestObject> {
public void serialize(CafRequestObject value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
final ToXmlGenerator xgen = (ToXmlGenerator) jgen;
xgen.writeStartObject();
addRequestMetaInfo(value.getRequestMetaInfo(),xgen);
addObject(value.getInputMap(), xgen,INPUT_MAP);
xgen.writeEndObject();
}
}
Is there any way to get the root name from a field value? This is mandatory in my situation, because every call must have a different root value (so no JsonRootName or XmlRootName is posssible for me)
Thanks a lot in advance!
I've finally found a solution. Maybe it is not the best, but at least works for me :)
I have extended jackson's class XmlSerializerProvider and overrided methods
serializeValue .
There is a section where code does the following stuff:
QName rootName = _rootNameFromConfig();
if (rootName == null) {
rootName = _rootNameLookup.findRootName(cls, _config);
}
I have modified it's original behaviour in order to alter the way it gets the root tag, based on the field of my root class:
QName rootName = _rootNameFromConfig();
if (rootName == null) {
if (value instanceof CafRequestObject){
rootName= getRootNameFromServiceId((CafRequestObject)value);
}
else{
rootName = _rootNameLookup.findRootName(cls, _config);
}
}
protected QName getRootNameFromServiceId(CafRequestObject object){
return new QName( object.getServiceId()+_SERVICE);
}
Thanks for your help!
I am using the #JsonProperty(name = "property_name") (Jackson) annotation wherever the property name in the json is different from the property name in my object. Is there a way I can programmatically access this information somewhere else in code?
Example:
public class Entity {
protected long entityName;
#JsonProperty("entity_name")
public long getEntityName() {
return entityName;
}
public void setEntityName(String entityName) {
this.entityName = entityName;
}
}
What I need is a method to map entity_name to entityName. e.g, getActualFieldName("entity_name") should return "entityName". I can keep a separate file with all these mappings, but I don't want to do that because this information is already present in the form of #JsonProperty annotations. If I can extract it from there somehow, it would simplify things a bit.
Check this Helper utility. getAnnotationParameter
I need to save some variables in a simple class using Java's XML Annotations: http://download.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/package-summary.html
Currently the class is pretty simple and looks like this:
#XmlRootElement
public class Chart {
#XmlElement
public String url;
#XmlElement
public String values;
#XmlElement
public String projectOrFilterName;
#XmlElement
public String countComplains;
public Chart(String url, String values, String projectOrFilterName, String countComplains) {
this.url = url;
this.values = values;
this.projectOrFilterName = projectOrFilterName;
this.countComplains = countComplains;
}
}
Now I need to save the data of a list as string variables with annotations like the existing ones. Im giving the constructor some kind of a list, let's say
List<Object>
The question is, how to extract all the variables out of it, and save their toString() representations with the given XML Annotations. If that's simpler one could assume, I get a List of Strings.
Can somebody please help me with this?
Who could have known, it would be that simple ;-)
#XmlElement
public List<String> data;
That's just, what I needed. The java script part, interpreting the xml creates an array out of the list's elements and everything is fine.
Thanks for your time thinking over my question!