Fail to convert xml into JAVA class - java

I have an xml response and want it to convert into java class for android usage.
<?xml version="1.0" encoding="UTF-8"?>
<title>
<results_info>
<page>1</page>
<total_pages>8</total_pages>
</results_info>
<listing_info>
<id>4</id>
<image></image>
</listing_info>
<listing_info>
<id>4</id>
<image></image>
</listing_info>
<listing_info>
<id>4</id>
<image></image>
</listing_info>
</title>
This is the code use for data fetch
public interface ApiService {
#GET("myUrl")
Call<MyObject> reqProfile();
}
Api WEB_SERVICE = new Retrofit.Builder()
.baseUrl("http://www.baseUrl.com/")
.client(client)
.addConverterFactory(SimpleXmlConverterFactory.create())
.build().create(Api.class);
how can I make a model class of MyObject for it

Thank you developers for interesting in the question, and finally after a lot of struggle I got to the point and solve this problem here is JAVA class of this xml.
I have created three classes for efficient work.
1: MyObject
public class MyObject {
#Element(name = "results_info")
private Results_info results_info;
#ElementList(inline = true)
List<Listing_info> listing_info;
}
2: Results_info
public class Results_info {
private String page;
private String total_pages;
}
3: Listing_info
public class Listing_info {
#Element(required = false)
String id;
#Element(required = false)
String image;
#Element(required = false)
}
#Element(required = false) is due to some time data may be empty.
And in response in call manager of retrofit, we can get all the data from accessing objects of these classes just like
...
if (response != null && response.isSuccessful() && response.body() != null) {
MyObject object = (MyObject) response.body();
String page = object.getResults_info().getPage();
List<Listing_info> myList = object.getList();
String a = myList.get(0).getImage();
}
...
Cheers.

Related

#CrossOrigin only works when register one domain

I wanted to make an connection between frontend and backend so I used #CrossOrigin annotation Like below.
#CrossOrigin(origins = "http://localhost:3000, http://server ip:3000, http://backend.com:3000")
#RestController
#RequestMapping("/member")
public class MemberController {
Logger logger = LoggerFactory.getLogger(MemberController.class);
private MemberService ms;
private Encryption encrypt;
private S3FileUploadService sfu;
#Autowired
public MemberController(MemberService ms, Encryption encrypt,S3FileUploadService sfu) {
this.ms = ms;
this.encrypt = encrypt;
this.sfu = sfu;
}
#GetMapping("/login")
public FrontMember login(#RequestHeader("Authorization") String autho) {
logger.info("Authorization : " + autho);
String memberInform = encrypt.aesDecrypt(autho);
String[] idPwd = memberInform.split("/");
Member loginTry = new Member();
loginTry.setEmail(idPwd[0]);
loginTry.setPwd(encrypt.shaEncryption(idPwd[1]));
Member authorizedUser = ms.login(loginTry);
if(authorizedUser == null){
logger.warn("No member info");
return null;
}else{
logger.info("Member Get : "+authorizedUser.toString());
String hashMemberNum = encrypt.aesEncrypt(Integer.toString(authorizedUser.getMemberNum()));
String mgHash = encrypt.aesEncrypt(Integer.toString(authorizedUser.getMg()));
FrontMember fm = new FrontMember(hashMemberNum, authorizedUser.getNickName(), authorizedUser.getPfUrl(),mgHash);
logger.info("Login User : "+fm.toString());
return fm;
}
}
}
But it doesn't work unless I only put one domain on origin like below.
#CrossOrigin(origins = "http://localhost:3000")
I want to put several domain at crossorigin.
How can I solve this problem?
Check the Official document of CrossOrign,we can found below description:
So the reason is that you have made a wrong invocation,if you need to allow multiple origins,you need to use an array contains of string instead of single string
In order to make your code work,you can try with below:
#CrossOrigin(origins = {"http://localhost:3000", "http://server ip:3000", "http://backend.com:3000"})
#RestController
#RequestMapping("/member")
public class MemberController {
}

Jackson XML deserialization of abstract type results in fields with null values

When trying to deserialize XML to an object that extends an abstract base class, I'm seeing that the list of references contains the expected number of elements, but all the fields on those objects are null.
This only happens if I create an XML reader for the abstract class. If I deserialize directly to the concrete implementation all the fields have the expected value.
I've added the minimum working example below
Expected output (as json for readability)
{
"References": [ { "id": "1", "Type": "Secondary Image" } ]
}
Actual output (as json for readability)
{
"References": [ { "id": null, "Type": null } ]
}
Test Data
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Foo TypeID="ConcreteA">
<Reference ID="1" Type="Secondary Image">
<Values/>
</Reference>
</Foo>
Abstract base class
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "TypeID", visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = ConcreteClassA.class, name = "ConcreteA")
})
public abstract class AbstractBase {
#JacksonXmlProperty(localName = "TypeID", isAttribute = true)
private String typeId;
#JsonIgnore
private List<Reference> references = new ArrayList<>();
#JacksonXmlElementWrapper(useWrapping = false)
#JacksonXmlProperty(localName = "Reference")
public List<Reference> getReferences() {
return references;
}
#JsonSetter
public AbstractBase setReferences(List<Reference> references) {
this.references.addAll(references);
return this;
}
}
Concrete Implementation
public class ConcreteClassA extends AbstractBase {}
Test Cases
public class DeserializationTest {
#Test
public void deserializedAbstractClass_fieldsShouldNotBeNull() throws JsonProcessingException {
var mapper = new XmlMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.deactivateDefaultTyping()
.registerModule(new JacksonXmlModule());
var xmlData = readTestData();
var reader = mapper.readerFor(AbstractBase.class);
var deserializedObject = reader.readValue(xmlData);
assert(deserializedObject instanceof ConcreteClassA);
var concreteClassA = (ConcreteClassA) deserializedObject;
assert(concreteClassA.getReferences().get(0).getId() != null);
}
#Test
public void deserializedConcreteClass_fieldsShouldNotBeNull() throws JsonProcessingException {
var mapper = new XmlMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.configure(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL, true)
.registerModule(new JacksonXmlModule());
var xmlData = readTestData();
var reader = mapper.readerFor(ConcreteClassA.class);
var deserializedObject = reader.readValue(xmlData);
assert(deserializedObject instanceof ConcreteClassA);
var concreteClassA = (ConcreteClassA) deserializedObject;
assert(concreteClassA.getReferences().get(0).getId() != null);
}
private String readTestData() {
try {
var datafile = getClass().getClassLoader().getResource("TestData.xml");
return Files.lines(Paths.get(datafile.toURI())).collect(Collectors.joining());
} catch (Exception e) { return ""; }
}
}
Turns out there are multiple problems, that I've managed to solve.
The Jackson version I was using (2.11) had some problems with multiple elements using the same tag, not in a wrapper. This is something I was aware of, and is the reason why my setter does "addAll" instead of just setting the list
This problem was solved by upgrading to 2.12, which means that it's no longer necessary to do this to handle unwrapped elements.
Jackson failed to properly deserialize the items, because the setter accepts a list, which apparently breaks due to some java generic mumbo jumbo (I was never able to figure out exactly why, just that it happens).
I solved this by having the setter accept a single element, and then adding that to the list
#JsonSetter(value = "Reference")
public AbstractBase setReferences(Reference reference) {
this.references.add(references);
return this;
}

How to unmarshall json lists using Spring Boot RestTemplate

I have to parse a REST response in json and it has a lot of nested lists with many objects.
The response contains an item called "ObjectList" which has a list and inside, two elements, "ObjectA" and "ObjectB". I don't know how to parse the response to objects using Jackson annotations.
The json looks like this:
"ObjectList": [
{
"ObjectA": {
"property1": false,
"property2": true
},
"ObjectB": {
"property1": 66,
"property2": true
},
{
"ObjectA": {
"property1": false,
"property2": true
},
"ObjectB": {
"property1": 66,
"property2": true
}
}
]
}
My code looks like this
ResponseEntity<Response> response = restTemplate.exchange(URL, HttpMethod.GET, request, Response.class);
Response response = response.getBody();
Response is:
#JsonIgnoreProperties(ignoreUnknown = true)
public class TimesheetListResponse {
#JsonProperty("ObjectA")
private List<ObjectA> objectAList;
#JsonProperty("ObjectB")
private List<ObjectB> objectBList;
That does not work at all, and I'm confused about how to map this.
According to your requirement the model structure may look like below. Within the objectList map in Response object, you need to add HashMap with keys as "ObjectA"/"ObjectB" string and value as instance of ObjectA/ObjectB. I have taken value type of Map as Object, so that any object type A/B can fit in there. Add corresponding #JsonXXX annotations.
public class Response {
private List<Map<String,Object>> objectList;
//Getters & Setters
}
public class ObjectB {
String propB1;
String propB2;
}
public class ObjectA {
String propA;
String propA1;
}
I also would consider the entry in the list as another wrapper object that can either ObjectA or ObjectB. I.e.
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Parent {
#JsonProperty("ObjectList")
private List<ChildWrapper> objectList = new ArrayList<>();
}
#JsonIgnoreProperties(ignoreUnknown = true)
public final class ChildWrapper {
#JsonProperty("ObjectA")
private Child ObjectA;
#JsonProperty("ObjectB")
private Child ObjectB;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Child {
#JsonProperty("property1")
private int property1;
#JsonProperty("property2")
private boolean property2;
}
It seems that the mapping was fine, I only had to initialize the Arraylist. The main issue was that the endpoint was returning empty because of a parameter that I forgot.

Android - SimpleXML framework cannot parse #ElementList

I am using SimpleXML framework to parse xmls in my Android application. I have a problem with getting #ElementList parsed correctly.
A fragment of xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SaleToPOIResponse>
<MessageHeader (...) />
<ReconciliationResponse ReconciliationType="SaleReconciliation">
<Response Result="Success"/>
<TransactionTotals PaymentInstrumentType="Card">
<PaymentTotals TransactionType="Debit" TransactionCount="182" TransactionAmount="4.17"/>
<PaymentTotals TransactionType="Credit" TransactionCount="1" TransactionAmount="2.01"/>
</TransactionTotals>
</ReconciliationResponse>
</SaleToPOIResponse>
My classes look:
ReconciliationResponseType.java:
#Root
#Order(elements = {
"Response",
"TransactionTotals"
})
public class ReconciliationResponseType {
#Element(name = "Response", required = true)
protected ResponseType response;
#ElementList(name = "TransactionTotals", inline = true, required = false)
protected List<TransactionTotalsType> transactionTotals;
#Attribute(name = "ReconciliationType", required = true)
protected String reconciliationType;
// getters and setters
}
TransactionTotalsType.java:
#Root
#Order(elements = {
"PaymentTotals",
"LoyaltyTotals"
})
public class TransactionTotalsType {
#ElementList(name = "PaymentTotals", inline = true, required = false)
protected List<PaymentTotalsType> paymentTotals;
#Attribute(name = "PaymentInstrumentType", required = true)
protected String paymentInstrumentType;
// getters and setters
}
I parse it using method:
public static SaleToPOIResponse fromXMLString(String xmlResponse) {
Reader reader = new StringReader(xmlResponse);
Serializer serializer = new Persister();
try {
SaleToPOIResponse response = serializer.read(SaleToPOIResponse.class, reader, false);
return response;
} catch (Exception e) {
Log.e(TAG, "Exception during parsing String XML to SaleToPOIResponse: ", e);
}
return null;
}
But every time I get an exception, that ordered element 'TransactionTotals' is missing, even though 1) it is not required 2) it does exist in the parsed xml
org.simpleframework.xml.core.ElementException: Ordered element 'TransactionTotals' missing for class pl.novelpay.epas.generated.saletopoimessages.ReconciliationResponseType
When I comment the 'TransactionTotals' from #Order the xml is parsed without an exception, but the TransactionTotals filed in result is empty. What am I missing here?
I found what was a problem while reading answer to a similar problem here:
https://sourceforge.net/p/simple/mailman/message/25699359/
I was using name insted of entry. So an ElementList attribute should look like this:
#ElementList(entry= "PaymentTotals", inline = true, required = false)
protected List<PaymentTotalsType> paymentTotals;
Now it works perfectly.

XMLRootElement converting a class to XML in jersey

I'm very good in converting a model class to JSON Array or Object.
But i'm a noob when it comes to XML.
I want my final output to be like this
<Response>
<Say voice="alice">Thanks for trying our documentation. Enjoy!</Say>
</Response>
To achieve it i create a model class
#XmlRootElement(name = "Response")
public class Response {
private Say say = new Say();
public Say getSay() {
return say;
}
public void setSay(Say say) {
this.say = say;
}
#XmlRootElement(name = "Say")
static class Say {
#XmlAttribute
private String voice = "alice";
private String string = "Thanks for trying our documentation. Enjoy!";
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
}
Now after converting it to XML with jersey my output was
<Response>
<say voice="alice">
<string>Thanks for trying our documentation. Enjoy!</string>
</say>
</Response>
I got a extra string tag. I'm not sure what attribute to set for the String so that it comes in the body.? Or is there any other way?
Also for say. The 'S' is not capitalised. How can i make it a capital letter?
Thanks in advance
By default properties and public fields will be mapped to elements. What you want to do is use #XmlValue to map the field to the element's value.
#XmlRootElement(name = "Say")
#XmlAccessorType(XmlAccessType.FIELD)
static class Say {
#XmlAttribute
private String voice = "alice";
#XmlValue
private String string = "Thanks for trying our documentation. Enjoy!";
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
Note the use of #XmlAccessorType(XmlAccessType.FIELD). This is so the default behavior doesn't "doubly" attempt to map the property defined by the getter and setter. Alternatively, you could place the annotations on the getter, and leave out the the #XmlAccessorType
Result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Response>
<say voice="alice">Thanks for trying our documentation. Enjoy!</say>
</Response>
public class ResponseTest {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Response.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Response response = new Response();
marshaller.marshal(response, System.out);
}
}
UPDATE
but can i know why the 'S' in Say is not capitalised even though #XmlRootElement(name = "Say") is specified?
You need to specify the name with #XmlElement(name = "Say") on the property. If you don't the default naming will kick in.
#XmlElement(name = "Say")
public Say getSay() {
return say;
}
The XmlRootElement(name = "Say") is only for if the element is used as the root element. For instance this:
Response.Say response = new Response.Say();
marshaller.marshal(response, System.out);
Would give you this output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Say voice="alice">Thanks for trying our documentation. Enjoy!</Say>

Categories

Resources