I am getting two wrapper elements in the XML output generated by Jackson.
I would like to have only one.
I have a Java bean
#Entity
#Table(name = "CITIES")
#JacksonXmlRootElement(localName = "City")
public class City implements Serializable {
private static final long serialVersionUID = 21L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JacksonXmlProperty(isAttribute = true)
private Long id;
#JacksonXmlProperty
private String name;
#JacksonXmlProperty
private int population;
// getters, setters etc
}
and a List wrapper class.
#JacksonXmlRootElement
public class Cities implements Serializable {
private static final long serialVersionUID = 22L;
#JacksonXmlProperty(localName = "City")
#JacksonXmlElementWrapper(localName = "MyCities")
private List<City> cities = new ArrayList<>();
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
}
I am getting this output, which has two wrapper elements. I would like
to remove one of them.
<Cities>
<MyCities>
<City id="1">
<name>Bratislava</name>
<population>432000</population>
</City>
<City id="2">
<name>Budapest</name>
<population>1759000</population>
</City>
<City id="3">
<name>Prague</name>
<population>1280000</population>
</City>
<MyCities>
</Cities>
One of them comes from the ArrayList, one from the class. How to get rid of one of the wrapper elements?
What I want to have is this:
<Cities>
<City id="1">
<name>Bratislava</name>
<population>432000</population>
</City>
<City id="2">
<name>Budapest</name>
<population>1759000</population>
</City>
<City id="3">
<name>Prague</name>
<population>1280000</population>
</City>
</Cities>
"Cities" is the root element, not a wrapper. Wouldn't removing the actual wrapper element "MyCities" work?
Adding #JacksonXmlElementWrapper(useWrapping = false) could also help.
Replacing #JacksonXmlElementWrapper(localName = "MyCities") with #JacksonXmlElementWrapper(useWrapping = false) in Cities should remove the additional wrapper element.
From the documentation:
#JacksonXmlElementWrapper
Allows specifying XML element to use for
wrapping List and Map properties; or disabling use (with 'useWrapping'
set to false).
The fix implemented in Cities:
#JacksonXmlRootElement
public class Cities implements Serializable {
private static final long serialVersionUID = 22L;
#JacksonXmlProperty(localName = "City")
#JacksonXmlElementWrapper(useWrapping = false)
private List<City> cities = new ArrayList<>();
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
}
You could also disable the wrapping functionality directly in the mapper with mapper.setDefaultUseWrapper(false);.
In this case you should simply remove #JacksonXmlElementWrapper(localName = "MyCities") from cities.
Related
My goal is to convert the XML to JavaObject and have the JavaObject be able to handle one or more "Tasks".
I would also like to be able to convert the JavaObject to JSON as well.
How can this be done? At it's current state, I am able to convert if only one task exists, anymore results in an error.
I have an XML file that follows this structure:
<Plan>
<ID></ID>
<Comment></Comment>
<CreateTime></CreateTime>
<Task>...</Task>
<Task>...</Task>
<Task>...</Task>
</Plan>
Each Task element is structured like so:
<Task>
<Type></Type>
<Time></Time>
<Target>
<Code></Code>
<TargetId></TargetId>
<Content>
<Stuff1></Stuff1>
<Stuff2></Stuff2>
<Stuff3></Stuff3>
</Content>
</Target>
</Task>
I am able to deserialize the XML when there is only one Task involved. Anymore gives me errors.
My Classes (simplified) are as follows:
#Root(name = "Plan")
public class Plan{
#Element(name = "ID")
private String id;
#Element(name = "Comment")
private String comment;
#Element(name = "CreateTime")
private String createTime;
#Element(name = "Task")
private Task task;
}
#Root(name = "Task")
public class Task{
#Element(name = "Type")
private String type;
#Element(name = "Time")
private String time;
#Element(name = "Target")
private Target target;
}
#Root(name = "Target")
public class Target{
#Element(name = "Code")
private String code;
#Element(name = "TargetId")
private String targetId;
#Element(name = "Content")
private Content content;
}
#Root(name = "Content")
public class Content{
#Element(name = "Stuff1")
private String stuff1;
#Element(name = "Stuff2")
private String stuff2;
#Element(name = "Stuff3")
private String stuff3;
}
In the schema file, I set this but doesn't seem to work:
<xs:element name="Task" type="Task" minOccurs="1" maxOccurs="unbounded">
At it's current state, I am able to convert if only one task exists,
anymore results in an error.
This happens because your element annotation interprets the task tag as a property, so if there is more than one it raises an error. To avoid this behaviour you can use the JacksonXmlElementWrapper annotation you can apply to your task list:
public class Plan {
#JacksonXmlProperty(localName = "Task")
#JacksonXmlElementWrapper(useWrapping = false)
List<Task> tasks;
}
public class Task {}
This above is a starting basic example you can use to define more complicated xmls.
I need to aggregate some data using java and Mongodb.
So my JS script it's that:
db.post.aggregate([
{$match: {date: {$gte: ISODate("2019-08-28T17:50:09.803Z"), $lte: ISODate("2019-12-03T21:45:51.412+00:00")}}},
{$project: {author: 1, _id: 0}},
{$group: {_id: "$author", count: {$sum:1}}}])
And my Java Code using spring+java is:
Aggregation aggregation =
newAggregation(
match(Criteria.where("date")
.lte(dynamicQuery.getEndDate())
.gte(dynamicQuery.getInitDate())),
project("author").andExclude("_id"),
group("author")
.count()
.as("total"));
AggregationResults<Post> result = mongoTemplate.aggregate(aggregation,Post.class, PostSummary.class);
List<Post> map = result.getMappedResults();
And I need to aggregate the sum of documents by authorId. My code returns the user code and no sum of documents.
And we have 2 Collections:
#EqualsAndHashCode(of = "id")
//TODO: Change the #Data to the properly scenario, we don't need
setters in this class anymore.
#Data
#Document (collection = "user")
//TODO: Change collection name to post, because collection are a group
of elements
public class User {
#Id
private String id;
private String name;
private String username;
#Email
private String email;
private String imageUrl;
private String providerId;
private LocalDateTime firstAccess;
}
Post Document:
#Data
#Document(collection = "post")
//TODO: Change collection name to post, because collection are a group of elements
public class Post {
public static final String AUTHOR_FIELD_NAME = "author";
public static final String DATE_FIELD_NAME = "date";
#Id
private String id;
private String content;
#DBRef(lazy = true)
#Field(AUTHOR_FIELD_NAME)
private User author;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#Field(DATE_FIELD_NAME)
private LocalDateTime date;
public Post(String content, User author, LocalDateTime date) {
this.content = content;
this.author = author;
this.date = date;
}
}
I have one solution, but I do not know if it is a better solution, so, let's go.
Because the Entity represented inside the Post Domain is one DBRef, the MongoDB does not parse the entire Stringo to the PostSummary.Class
#Getter
#Setter
public class PostSummary {
private User author;
private int count;
}
So, for the solution, I only parse the #Id in the author, and it's working. So If anyone has a better solution we have one solution now.
And should be like this:
#Getter
#Setter
public class PostSummary {
#Id
private User author;
private int count;
}
I have XML input that could basically look like this:
<mxGraphModel>
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<object label="" Property1="43" id="2">
<mxCell style="whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="196" y="161" width="200" height="30" as="geometry"/>
</mxCell>
</object>
<object label="" Property2="Helloooo" id="3">
<mxCell style="whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="192" y="269" width="200" height="30" as="geometry"/>
</mxCell>
</object>
<object label="" Property3="23" id="4">
<mxCell style="whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="210" y="383" width="200" height="30" as="geometry"/>
</mxCell>
</object>
</root>
</mxGraphModel>
I'm trying to parse this input via JAXB:
MxGraphModel.java:
#XmlRootElement(name = "mxGraphModel")
#XmlAccessorType(XmlAccessType.FIELD)
public class MxGraphModel {
#XmlElementWrapper(name = "root")
#XmlElement(name = "object")
private ArrayList<MxObject> mxObjects;
#XmlElement(name = "mxCell")
private ArrayList<MxCell> mxCells;
//getters and setters...
}
MxObject.java:
#XmlAccessorType(XmlAccessType.FIELD)
public class MxObject {
#XmlAttribute
private String id;
#XmlAttribute
private String label;
#XmlAttribute
private String documentName;
#XmlAttribute
private String documentDescription;
#XmlElement
private MxCell mxCell;
//getters and setters...
}
MxCell.java:
#XmlAccessorType(XmlAccessType.FIELD)
public class MxCell {
#XmlAttribute
private String id;
#XmlAttribute
private String parent;
#XmlAttribute
private String value;
#XmlAttribute
private String style;
#XmlAttribute
private String vertex;
#XmlElement(name = "mxGeometry")
private MxGeometry geometry;
//getters and setters...
}
DemoController.java:
#RestController
#RequestMapping("/xml")
public class DemoController {
#RequestMapping(method = RequestMethod.POST, consumes = {MediaType.APPLICATION_XML_VALUE})
public void parseXML(#RequestBody MxGraphModel mxGraphModel) {
//mxGraphModel contains EITHER objects of type MxObject
//OR
//MxCell
}
}
My problem is, that the parsing operation results in an MxGraphModel object that contains either a collection of objects OR a collection of mxCells - but never both.
It seems to me that the order in which I declare the XmlElements ("object" and "mxCell") is relevant for the parsing result. When I declare mxCell before object, 2 mxCells are being parsed (which is correct), but the object collection remains null. And the other way around.
Is it possible to have both XmlElements parsed? I don't know whether the issue is related to the fact that an object element could contain an mxCell element itself...
Thanks.
#Dimpre Jean-Sébastien:
You're totally right, sorry for posting the answer this late.
The solution is not to use the root ElementWrapper
So instead of using
#XmlElementWrapper(name = "root")
I had to create a root XmlElement in MxGraphModel.java:
#XmlElement(name = "root")
private MxRoot mxRoot;
Furthermore I created a file called MxRoot.java and moved all collections inside it:
MxRoot.java:
#XmlAccessorType(XmlAccessType.FIELD)
public class MxRoot {
#XmlElement(name = "UserObject")
private ArrayList<MxObject> mxUserObjects;
#XmlElement(name = "object")
private ArrayList<MxObject> mxObjects;
//XmlElement sets the name of the entities
#XmlElement(name = "mxCell")
private ArrayList<MxCell> mxCells;
//getters and setters...
}
It seems the #XmlWrapper Annotation expects exactly one collection. It is not meant to wrap around multiple collections (makes sense now that I know it :))
Thanks to all!
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<batch-execution>
<insert out-identifier="employee" return-object="true" entry-point="DEFAULT">
<fact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="employee">
<name>Rajashekhar</name>
<age>21</age>
</fact>
</insert>
<fire-all-rules></fire-all-rules>
</batch-execution>
Now I am getting this output as above but I want out put like below
<batch-execution>
<insert out-identifier="employee" return-object="true"
entry-point="DEFAULT">
<com.practise.Employee>
<name>Rajashekhar</name>
<age>21</age>
</com.practise.Employee>
</insert>
<fire-all-rules />
</batch-execution>
My Jaxb classes are
Request.java
#XmlRootElement(name = "batch-execution")
#XmlAccessorType(XmlAccessType.FIELD)
public class Request implements Serializable {
#XmlElement(name = "insert")
private List<Insert> insert;
#XmlElement(name = "fire-all-rules",nillable=true)
private String fireAllRules = "";
.
..
setters and getter
Insert.java
#XmlAccessorType(XmlAccessType.FIELD)
public class Insert {
#XmlAttribute(name = "out-identifier", required = true)
private String outIdentifier;
#XmlAttribute(name = "return-object")
private boolean returnObject;
#XmlAttribute(name = "entry-point")
private String entryPoint;
private Object fact;
.
.
setters and gettes
com.practise.Employee.java
#XmlRootElement(name="kewill.com.kewill.practoise.Employee")
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee implements java.io.Serializable
{
static final long serialVersionUID = 1L;
#org.kie.api.definition.type.Label("Name")
private java.lang.String name;
#org.kie.api.definition.type.Label("Id")
private java.lang.Integer id;
#org.kie.api.definition.type.Label("Age")
private int age;
#org.kie.api.definition.type.Label(value = "valid")
private java.lang.Boolean valid;
.
. setters and getters
I think it is possible through xtream api but I want to use JAXB please provide me the solution in jaxb.
In Insert.java
added Annotation for
private Object fact;
as
#XmlAnyElement(lax = true)
private Object fact;
Now it is giving expected output.
<?xml version='1.0'?>
<info>
<contract>
<symbol>IBM</symbol>
<sectype>STK</sectype>
<exchange>SMART</exchange>
<currency>USD</currency>
</contract>
<order>
<action>SELL</action>
<quantity>100</quantity>
<ordertype>LMT</ordertype>
<imtprice>imtprice</imtprice>
<transmit>false</transmit>
</order>
</info>
I want to use jaxb annotations with existing java classes to create above XML input but i don't know how create nested xml structure based on Java classes
Try this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder =
{"contract", "order"}) public class Info
{ #XmlElement(required =
true) private Contract
contract; #XmlElement(required = true) private Order order;
// Getters and setters }
Another class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {"symbol",
"sectype", "exchange",
"currency"}) public class
Contract { #XmlElement(required
= true) private String symbol; #XmlElement(required =
true) private String
sectype; #XmlElement(required =
true) private String
exchange; #XmlElement(required
= true) private String currency; //Getters and
setters}
Create an order class the same way.