Polymorphism in XStream serialization and deserialization - java

I have these classes:
#XStreamAlias("person")
public class PersonConfig {
private AnimalConfig animalConfig;
}
public interface AnimalConfig {}
#XStreamAlias("dog");
public class DogConfig extend AnimalConfig {}
#XStreamAlias("cat");
public class CatConfig extend AnimalConfig {}
And I would like to be able to deserialize this xml with the classes above:
<person>
<dog/>
<person>
As well as deserialize this xml too, with the same classes:
<person>
<cat/>
<person>
So that in both cases, the PersonConfig's field animalConfig is filled. In the first XML with a DogConfig instance and in the second XML with a CatConfig instance.
Is this possible by adding some annotation to make this work?

It seems XStream does not allow you to do it easily.
Your question is similar to this one, asking for managing something like a xsd:choice with XStream.
If you don't necessarily need to use XStream, JAXB will allow you to do it easily :
#XmlRootElement(name="person")
public class PersonConfig {
private AnimalConfig animalConfig;
#XmlElementRefs({
#XmlElementRef(name="cat", type=CatConfig.class),
#XmlElementRef(name="dog", type=DogConfig.class)
})
public AnimalConfig getAnimalConfig() {
return animalConfig;
}
public void setAnimalConfig(AnimalConfig animalConfig) {
this.animalConfig = animalConfig;
}
}
After some researches, listing all available classes for your property can be avoided if you choose to use the XmlAdapter.
In Blaise Doughan link, the example uses an abstract class, not an interface.
Edit :
As Blaise Doughan said in its comment, #XmlElementRef is better suited for this purpose. Code has been updated accordingly.

You can write a converter.
public class CustomConverter implements Converter {
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
// TODO: Get annotation value from object 'source' with name of tag via Reflection.
// Or add a method to the AnimalConfig interface giving you tag name to put to serialization output.
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
// TODO: use reflection to create animal object based on what you xml tag you have at hahd.
return context.convertAnother(context.currentObject(), SomeAnimalClazz.class);
}
public boolean canConvert(Class type) {
return type.equals(AnimalConfig.class);
}
}
There's a disadvantage: polymorphism will require you to use Java Reflection API and performance degradation.

This is quite easy. You just have to do it right and not like my previous speakers. When you process the annotations, XStream can assign those classes.
#XStreamAlias("person")
public class PersonConfig {
private AnimalConfig animalConfig;
public String toXml() {
XStream xstream = new XStream();
xstream.processAnnotations(DogConfig.class);
xstream.processAnnotations(CatConfig.class);
return xstream.toXML(this);
}
}
public interface AnimalConfig {}
#XStreamAlias("dog");
public class DogConfig implements AnimalConfig {}
#XStreamAlias("cat");
public class CatConfig implements AnimalConfig {}

It works out of the box, with out any annotations...
private static interface Test {
String getName();
Params getParams();
}
private static interface Params {
}
private static class OneParams implements Params {
private String oneValue;
public String getOneValue() {
return oneValue;
}
public void setOneValue(String oneValue) {
this.oneValue = oneValue;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OneParams [oneValue=");
builder.append(oneValue);
builder.append("]");
return builder.toString();
}
}
private static class TwoParams implements Params {
private String twoValue;
public String getTwoValue() {
return twoValue;
}
public void setTwoValue(String twoValue) {
this.twoValue = twoValue;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("TwoParams [twoValue=");
builder.append(twoValue);
builder.append("]");
return builder.toString();
}
}
private static class OneTest implements Test {
private String name;
private Params params;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public Params getParams() {
return params;
}
public void setParams(Params params) {
this.params = params;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OneTest [name=");
builder.append(name);
builder.append(", params=");
builder.append(params);
builder.append("]");
return builder.toString();
}
}
---- now deserialize like this...
System.out
.println(ser
.deserialize("<XStreamTest_-OneTest><name>OneTest</name><params class=\"XStreamTest$OneParams\"><oneValue>1</oneValue></params></XStreamTest_-OneTest>"));
System.out
.println(ser
.deserialize("<XStreamTest_-OneTest><name>TwoTest</name><params class=\"XStreamTest$TwoParams\"><twoValue>2</twoValue></params></XStreamTest_-OneTest>"));

Related

JAVA How can i get a method to accept a parent class and all of it's extended classes?

I apologize if this has been answered before but either i don't know the correct verbiage or my google fu is bad.
I have a TestModel class which has the getters and setters for all the tests I use. Then I have a AdditionalTestModel class that extends the TestModel with additional getters and setters for that specific type of tests.
Now I have BuildTest Class that i want to be able to pass TestModel and any extended classes of TestModel.
public static Class<?> buildTest(Class<?> test, Class<?> template)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class<?> testClass = test.getClass();
Method[] testMethods = testClass.getMethods();
for (Method method : testMethods) {
String name = method.getName();
if (name.startsWith("get")) {
String testMethodType = method.getReturnType().getTypeName();
// additional code removed//
}
}
If instead of Class<?> i was using TestModel it would work for any test that i pass of Class type TestModel. But i want to be able to pass the extended class to this method as well without having to write a method for each extended class. Any recommendations?
Adding information on the models in case it matters.
public class TestModel {
private String testDescription;
private String testName;
private String apiPath;
private String method;
private String expectedTest;
private Map<String, String> header = new HashMap<>();
private Object body;
private String expectedResult;
private String testCaseId;
private String testUUID;
private List testTypes;
public String getTestDescription() {
return testDescription;
}
public void setTestDescription(String testDescription) {
this.testDescription = testDescription;
}
public String getTestName() {
return testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
public String getAPIPath() {
return apiPath;
}
public void setAPIPath(String apiPath) {
this.apiPath = apiPath;
}
public String getExpectedTest() {
return expectedTest;
}
public void setExpectedTest(String testName) {
this.expectedTest = testName;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Map<String, String> getHeader() {
return header;
}
public void setHeader(Map<String, String> header) {
this.header = header;
}
public Object getBody() {
return body;
}
public void setBody(Object body) {
this.body = body;
}
public String getExpectedResult() {
return expectedResult;
}
public void setExpectedResult(String expectedResult) {
this.expectedResult = expectedResult;
}
public String getTestCaseId() {
return testCaseId;
}
public void setTestCaseId(String testCaseId) {
this.testCaseId = testCaseId;
}
public String getTestUUID() {
return testUUID;
}
public void setTestUUID(String testUUID) {
this.testUUID = testUUID;
}
public List getTestTypes() {
return testTypes;
}
public void setTestTypes(List testTypes) {
this.testTypes = testTypes;
}
}
public class AdditionalTestModel extends TestModel {
#Override public Object getBody() {
return super.getBody();
}
}
Edit: per a request adding the call information here:
#Test(dataProvider = "Default", threadPoolSize = THREADS, timeOut = API_TIME_OUT)
#Description("")
public void sampleTest(AdditionalTestModel testFromDataProvider) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
testSetup(testFromDataProvider);
AdditionalTestModel test = BuildTest.buildTest(testFromDataProvider, template);
Response response = RestAPI.call(test, testEnvironment);
if (null != response) {
ValidateAPIResponse.validateTestModel(test, response);
} else {
Assert.fail("Response is null, probably a bad method.");
}
}
Where testFromDataProvider is passed from a TestNg data provider.
Now LppEdd below already pointed out i could only assign the base class using generics so working on trying it his way, just have not gotten a chance to change things up yet.
Edit: Also realize now my question was bad. Thanks LppEdd. I should have asked How can I get a method to accept an instance of a class and an instance of any extended class
You are close, you just need to use the extends modifier.
If the class passed in as the test and template parameter should be the same exact class type, you can do:
public static <T extends TestModel> Class<T> buildTest(Class<T> test, Class<T> template) { ... }
Otherwise you can do
public static Class<? extends extends TestModel> buildTest(Class<? extends TestModel> test, Class<? extends String> extends TestModel) { ... }
Which will allow different types to be returned and passed in to each parameter.
You can read up on Java generics and wilcards starting here: https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
Your buildTest method must accept a TestModel class.
You might be looking for something like
public static TestModel buildTest(
final TestModel test,
final TestModel template) {
final Class<? extends TestModel> testClass = test.getClass();
final Method[] testMethods = testClass.getMethods();
for (final Method method : testMethods) {
final String name = method.getName();
if (name.startsWith("get")) {
final String testMethodType = method.getReturnType().getTypeName();
// additional code removed
}
}
// Maybe
return yourNewInstance; // yourNewInstance is a TestModel, or any class extending it
}
The template argument seems unused here (clarify).
What's the wanted return type? (clarify)
Usage example
final TestModel value1 = buildTest(new TestModel(), ...);
final TestModel value2 = buildTest(new AdditionalTestModel(), ...);
This looks to be exactly the same problem as must be solved by test frameworks. For example, see junit (https://junit.org/junit5/).
The core problem is how to obtain the collection of test methods of a class.
A direct solution would be to have the test class be required to answer its test methods, say, Collection<Function<Void, Void>> getTests(); This has several problems, one being that sub-classes must explicitly list their test methods, two being that sub-classes must be careful to add in the test methods from their super-class, and third, this really fits more as static behavior, which would try to shift java instance typing to the class layer, which just isn't supported by java.
An indirect solution would be to require that test methods satisfy a particular pattern (for example, must start with "test" and have no parameters), and use reflection to discover the methods. Or, use an annotation (say, #Test, which is what junit does) to mark out test methods, and again use the java reflection API to discover methods with the marker.

Optimizing copied code in Java, avoiding if clause and casting

I have two classes:
DocumentState and ElectronicDocumentState.
They look like this:
public class DocumentState extends LabeledEnum {
public static final DocumentState CREATED = new DocumentState("created");
// ..... - 15 other statuses
}
ElectronicDocumentState also extends LabeledEnum and has its own statuses, some are common like created, other are unique.
Then I have plenty of methods in code that take DocumentState as a parameter or are returning DocumentState as result. Now they should work also with ElectronicDocumentState.
Also I have plenty places that do:
if (DocumentSate.CREATED.equals(doc.getState()) || DocumentState.DELETED.equals(doc.getState())) {
//do something with document
}
I want to avoid 'if' and avoid creating new methods for ElectronicDocumentState as more state can appear in future.
How would you do that ?
So using the below example, how would you refactor it so it could work with DocumentState and ElectronicDocumentState? I have plenty of such methods that now should also work with ElectronicDocumentState. Someone asks us to mix electronicDocuments with documents in business logic:
private DocumentState getDocumentStateForDetails(Document doc, DocumentState sourceState) {
if (DocumentState.CREATED.equals(doc.getDocumentState())) {
if (sourceState.equals(DocumentState.CREATED)) {
return DocumentState.CREATED;
} else {
return DocumentState.BLOCKED;
}
} else {
return sourceState.getDocumentState();
}
}
If you are worrying about further extension of your state model, I'd suggest you to think about using inheritance instead of if/switch and instanceof by splitting the document class to Value and State, for instance. If you have the same set of available actions for any state of document, just use classical State pattern, otherwise, each State may have its own set of available actions:
public class Document {
public static CreatedDocument<Document> create(String author) {
return new CreatedDocument<>(new Document(author));
}
private String author;
//...
private Document(String author) {
//...
}
}
public class ElectronicDocument extends Document {
public static CreatedElectronicDocument create(String author, String url) {
return new CreatedElectronicDocument(author, url);
}
private String url;
//...
public ElectronicDocument(String author, String url) {
//...
}
//...
}
public interface DocumentState<T extends Document> {
T getDocument();
char getCode(); // might be needed for something like logging?
}
public abstract class AbstractDocumentState<T extends Document> implements DocumentState<T> {
protected final T document;
protected AbstractDocumentState(T document) {
this.document = document;
}
#Override
public T getDocument() {
return document;
}
}
public class CreatedDocument<T extends Document> extends AbstractDocumentState<T> {
public CreatedDocument(T document) {
super(document);
}
#Override
public char getCode() {
return 'C';
}
public DocumentState<T> delete() {
return new DeletedDocument<>(document);
}
}
public class CreatedElectronicDocument extends CreatedDocument<ElectronicDocument> {
public CreatedElectronicDocument(String author, String url) {
super(new ElectronicDocument(author, url));
}
public DownloadElectronicDocument download() {
return new DownloadElectronicDocument(document);
}
}
public class DownloadElectronicDocument extends AbstractDocumentState<ElectronicDocument> {
public DownloadElectronicDocument(ElectronicDocument document) {
super(document);
// DO DOWNLOAD HERE
}
#Override
public char getCode() {
return 'L';
}
public DocumentState<ElectronicDocument> delete() {
return new DeletedDocument<>(document);
}
}
public class DeletedDocument<T extends Document> extends AbstractDocumentState<T> {
public DeletedDocument(T document) {
super(document);
// DO DELETE HERE
}
#Override
public char getCode() {
return 'D';
}
}
Not sure you need getCode() now, when you use inheritance. BTW, switch works faster than a set of if/else if.
If you'd like to stay with your enum classes, why wouldn't to extract common states to a super class?
public class CommonDocumentState extends LabeledEnum {
public static final CommonDocumentState CREATED = new CommonDocumentState ("created");
..... - n other statuses
}
public class DocumentState extends CommonDocumentState {
..... - m other statuses
}
public class ElectronicDocumentState extends CommonDocumentState {
..... - k other statuses
}
That's the only way to have such generic rules like
if (DocumentSate.CREATED.equals(doc.getState()) || DocumentState.DELETED.equals(doc.getState())) {
//do something with document
}
be working for both DocumentState and ElectronicDocumentState.
There are not enough information about your domain to provide a final answer, but I have some suggestions:
it seems that both DocumentState and ElectronicDocumentState inherit from LabeledEnum; if you want to manage both of them in your methods you can make ElectronicDocumentState inherit from DocumentState of it's feasibile merge the two classes. This will allow to pass ElectronicDocumentState or DocumentState in your methods and perhaps solve your second question.
if you want to avoid the if you can build a list of allowed method and check against the list, something like:
L
public class YourClass {
List<DocumentSate> allowedStates=//init here or in constructor
....
public void yourMethod(....) {
if (allowedStates.contains(doc.getState())) {
//do something
}
}
allowedStates may be factored out in separate class if it is a common case. If you find the refactoring feasible, may be you can check if you are dealing with a finite state machine and implement it (with the help of some exisiting libraries).

Why Google Gson.toJson loses data

I have five classes:
Comment,Paper,WoundPaper,Document,WoundDoc.
Comment is a holder for text.
Paper is empty and abstract class.
WoundPaper extends Paper and stores a String and an ArrayList of Comments.
Document is abstract class and stores ArrayList of <? extends Paper>.
WoundDoc extends Document.
You can see those classes below:
Comment class:
public class Comment {
private final String text;
public static class Builder {
private final String text;
public Builder(String text) {
this.text = text;
}
public Comment build(){
return new Comment(this);
}
}
private Comment(Builder builder) {
this.text = builder.text;
}
public String getText() {
return text;
}
}
Paper class:
public abstract class Paper {
protected Paper(ArrayList<Comment> commentList) {
}
}
WoundPaper class:
public class WoundPaper extends Paper {
private final String imageUri;
private final ArrayList<Comment> commentList;
public static class Builder {
private final String imageUri;
private final ArrayList<Comment> commentList;
public Builder(String imageUri, ArrayList<Comment> commentList) {
this.imageUri = imageUri;
this.commentList = commentList;
}
public WoundPaper build() {
return new WoundPaper(this);
}
}
private WoundPaper(Builder builder) {
super(builder.commentList);
this.imageUri = builder.imageUri;
this.commentList = builder.commentList;
}
}
Document class:
public abstract class Document {
private final ArrayList<? extends Paper> paperList;
protected Document(ArrayList<? extends Paper> paperList) {
this.paperList = paperList;
}
}
WoundDoc class:
public class WoundDoc extends Document {
public static class Builder {
private final ArrayList<WoundPaper> paperList;
public Builder(ArrayList<WoundPaper> paperList) {
this.paperList = paperList;
}
public WoundDoc build() {
return new WoundDoc(this);
}
}
private WoundDoc(Builder builder) {
super(builder.paperList);
}
}
Now I have to create an instance of WoundDoc and convert it to JSON string by Gson.This is a sample code to do that:
Comment comment = new Comment.Builder("comment").build();
ArrayList<Comment> commentList = new ArrayList<Comment>();
commentList.add(comment);
commentList.add(comment);
WoundPaper woundPaper = new WoundPaper.Builder("some Uri", commentList).build();
ArrayList<WoundPaper> woundPaperList = new ArrayList<WoundPaper>();
woundPaperList.add(woundPaper);
woundPaperList.add(woundPaper);
WoundDoc woundDoc = new WoundDoc.Builder(woundPaperList).build();
System.out.println("woundDoc to JSON >> " + gson.toJson(woundDoc));
But output is strange:
woundDoc to JSON >> {"paperList":[{},{}]}
As I displayed before,WoundDoc stores list of WoundPaper and each WoundPaper stores list of comments.But why there is no comment in output?
When gson goes to serialise the WoundDoc all it can tell is that there is a List of two objects of type something which extends Paper (List<? extends Paper>); the specific type is unknown. As Paper has no fields for gson to work with it can only say that there are two entries within that list, but as they are the type Paper, which has no fields, there is no way to work out how to serialize those objects.
A way to resolve this is to pass type from your implementations to the abstract classes so that when gson inspects them it can see which class the objects it encounters are instances of, and so work out how to serialise them.
Update Document to take a type parameter:
public abstract class Document<T extends Paper> {
private final ArrayList<T> paperList;
protected Document(ArrayList<T> paperList) {
this.paperList = paperList;
}
}
Update WoundDoc to pass type to Document:
public class WoundDoc extends Document<WoundPaper> {
Another way to resolve it if you are unable to make the above changes would be to write a custom serializer for WoundDoc
Personally I'd use the first solution and pass type, because I'm lazy and writing a custom serializer is more effort
edit: Minor shout out to jackson which will throw an exception if you try to serialise something and it cannot work out how to do it.

Java interface access different classes by calling same interface

I want to use java interface in a way that i will make a call defining interface in my other class like 'private SoapURL soapURL;' and than i can access any class's method for example : i want to use login:-
private SoapURL soapURL;
SoapUrl = LoginSoap ();
String nameSpace = soapURL.getMethodName();
String url = soapURL.getUrl();
Is there any way to do something like this. I am sorry i am not very good with Object Oriented principles but if there is a solution for my problem i would like to know it. Thanks in advance.
public interface SoapURL {
public String getNameSpace();
public String getUrl();
public String getSoapAction();
public String getMethodName();
public String getTag();
}
LoginSoap class
public class LoginSoap implements SoapURL {
#Override
public String getNameSpace() {
return "https://host.com/MobileWFC/";
}
#Override
public String getUrl() {
return "https://host.com/MobileWFC/MobileWS.asmx";
}
#Override
public String getSoapAction() {
return "https://host.com/MobileWFC/UserControl";
}
#Override
public String getMethodName() {
return "UserControl";
}
#Override
public String getTag() {
return "Login Activity";
}
}
SignUpSoap class
public class SignUpSoap implements SoapURL {
#Override
public String getNameSpace() {
return "https://host.com/MobileWFC/";
}
#Override
public String getUrl() {
return "https://host.com/MobileWFC/MobileWS.asmx";
}
#Override
public String getSoapAction() {
return "https://host.com/MobileWFC/UserRegister";
}
#Override
public String getMethodName() {
return "UserRegister";
}
#Override
public String getTag() {
return "SignUp Activity";
}
}
ResetPasswordSoap class
public class ResetPasswordSoap implements SoapURL {
#Override
public String getNameSpace() {
return "https://host.com/MobileWFC/";
}
#Override
public String getUrl() {
return "https://host.com/MobileWFC/MobileWS.asmx";
}
#Override
public String getSoapAction() {
return "https://host.com/MobileWFC/UserPasswordReset";
}
#Override
public String getMethodName() {
return "UserPasswordReset";
}
#Override
public String getTag() {
return "Forget Password Activity";
}
}
Your implementation looks correct. To make use of it, you can do this in main:
SoapURL reset = new ResetPasswordSoap();
System.out.println(reset.getUrl());
This is a method of minimizing coupling in large systems. And reduces dependency between objects by making use of a common interface for groups of objects that work together. You might be new at Object oriented principles, but you are one step ahead of the game already
To pass it to a function, you do:
public JPanel resetPass(SoapURL reset) {
...
}
// In main:
JPanel resetPassPanel = resetPass(reset);
Just do, for example:
SoapURL example = new LoginSoap();
String a = example.getTag();
a should be equal to "Login Activity"
The main use of Interface is polymorphism, or the ability to perform the same
operation on a number of different objects,
which is exactly what you wanted in your scenario
Your approach is absolutely fine , just a modification needed
private SoapURL soapURL;
//SoapUrl = LoginSoap (); // This line should be replaced with the Below line
soapURL=new LoginSoap();
String nameSpace = soapURL.getMethodName();
String url = soapURL.getUrl();
Since LoginSoap, SignUpSoap,ResetPasswordSoap classes are implemented classes of SoapURL Interface , thus reference variable of SoapURL can store Object of any of these child classes
soapURL=new LoginSoap();//soapURL.someMethod will call method of LoginSoapClass
soapURL=new SignUpSoap();// will call method of SignUpSoap class
soapURL=new ResetPasswordSoap();

Force Jackson to add addional wrapping using annotations

I have the following class:
public class Message {
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
When converting the instance to JSON using Jackson by default I get:
{"text":"Text"}
I would like to get:
{"message":{"text":"Text"}}
Is there any JAXB / Jackson annotation I can use to achieve my goal?
As a workaround, I can wrap my class with another class:
public class MessageWrapper {
private Message message;
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
}
or a more generic solution:
public class JsonObjectWrapper<T> {
/**
* Using a real map to allow wrapping multiple objects
*/
private Map<String, T> wrappedObjects = new HashMap<String, T>();
public JsonObjectWrapper() {
}
public JsonObjectWrapper(String name, T wrappedObject) {
this.wrappedObjects.put(name, wrappedObject);
}
#JsonAnyGetter
public Map<String, T> any() {
return wrappedObjects;
}
#JsonAnySetter
public void set(String name, T value) {
wrappedObjects.put(name, value);
}
}
Which can be used like so:
Message message = new Message();
message.setText("Text");
JsonObjectWrapper<Message> wrapper = new JsonObjectWrapper<Message>("message", message);
Is there any JAXB / Jackson annotation I can use to achieve my goal?
Thanks.
With Jackson 2.x use can use the following to enable wrapper without adding addition properties in the ObjectMapper
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
#JsonTypeName(value = "student")
public class Student {
private String name;
private String id;
}
On workaround: you don't absolutely need those getters/setters, so could just have:
public class MessageWrapper {
public Message message;
}
or perhaps add convenience constructor:
public class MessageWrapper {
public Message message;
#JsonCreator
public MessageWrapper(#JsonProperty("message") Message m) {
message = m;
}
}
There is a way to add wrapping too; with 1.9 you can use SerializationConfig.Feature.WRAP_ROOT_ELEMENT and DeserializationConfig.Feature.UNWRAP_ROOT_ELEMENT. And if you want to change the wrapper name (by default it is simply unqualified class name), you can use #JsonRootName annotation
Jackson 2.0 adds further dynamic options via ObjectReader and ObjectWriter, as well as JAX-RS annotations.
It was sad to learn that you must write custom serialization for the simple goal of wrapping a class with a labeled object. After playing around with writing a custom serializer, I concluded that the simplest solution is a generic wrapper. Here's perhaps a more simple implementation of your example above:
public final class JsonObjectWrapper {
private JsonObjectWrapper() {}
public static <E> Map<String, E> withLabel(String label, E wrappedObject) {
HashMap<String, E> map = new HashMap<String, E>();
map.put(label, wrappedObject);
return map;
}
}
Provided you don't mind the json having a capital m in message, then the simplest way to do this is to annotate your class with #JsonTypeInfo.
You would add:
#JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
public class Message {
// ...
}
to get {"Message":{"text":"Text"}}
A Simpler/Better way to do it:
#JsonRootName(value = "message")
public class Message { ...}
then use
new ObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true).writeValueAs...
If using spring, then in application.properties file add following:-
spring.jackson.serialization.WRAP_ROOT_VALUE=true
And then use #JsonRootName annotation on any of your class that you wish to serialize. e.g.
#JsonRootName("user")
public class User {
private String name;
private Integer age;
}
I have created a small jackson module that contains a #JsonWrapped annotation, that solves the problem. See here for the code: https://github.com/mwerlitz/jackson-wrapped
Your class would then look like:
public class Message {
#JsonWrapped("message")
private String text;
}

Categories