I'm currently using the simple XML library, and the tutorial didn't have a runnable example for ElementLists. http://simple.sourceforge.net/home.php
I have an example class:
#Root
public class Example {
#ElementList
private List<String> text;
#Attribute
private int index;
public Example() {
super();
}
public Example(List<String> text, int index) {
this.text = text;
this.index = index;
}
public List<String> getMessage() {
return text;
}
public int getId() {
return index;
}
}
And a simple class for running:
public class M {
public static void main(String[] args) throws Exception {
Serializer serializer = new Persister();
List<String> l = new LinkedList<String>();
l.add("f");
l.add("f");
Example example = new Example(l, 123);
File result = new File("example.xml");
serializer.write(example, result);
}
}
The XML that I generate is:
<example index="123">
<text class="java.util.LinkedList">
<string>f</string>
<string>f</string>
</text>
</example>
Why am I getting the class="java.util.LinkedList"? I'm confused on how remove this attribute.
You can use the VisitorStrategy to intercept the serialization of the object.
Strategy strategy = new VisitorStrategy(new Visitor() {
#Override
public void write(Type type, NodeMap<OutputNode> node) throws Exception {
if ("text".equals(node.getName())){
node.remove("class");
}
}
...
});
I was working on the same problem and I got a way to avoid the 'class' attribute.
Instead of using #ElementList like this:
#ElementList(name="Files", entry="File")
You can use #Path annotation with the #ElementList as follows:
#Path(value="Files")
#ElementList(inline=true, entry="File")
You can use an Implementation for an #ElementList List, example :
LinkedList instead of List.
#ElementList
private LinkedList<String> texts;
This will avoid class attribute not wanted.
Related
To the best of my understanding, Jackson will
serialize a public instance variable to the variable name
public List<String> myStrings = new ArrayList<>();
serializes to
{ 'myStrings' : [ ... ] }
serialize a private instance variable to the variable name if it has a public getter named getVariable():
private List<String> myStrings = new ArrayList<>();
public List<String> getMyStrings() { return myStrings; }
serializes similar to
{ 'myStrings' : [ ... ] }
However, what I am trying to achieve is to serialize it to a String (instead of array of Strings) based on another method, but keep the JSON key (based on #JsonInclude(JsonInclude.Include.NON_NULL) suppressing the original accessor in some cases
#JsonInclude(JsonInclude.Include.NON_NULL)
private boolean firstStringOnly = true;
private List<String> myStrings = new ArrayList<>();
public List<String> getMyStrings() { return firstStringOnly ? null: myStrings; }
public String getFirstString() { return firstStringOnly ? myStrings.get(0) : null; }
Desired JSON serialization:
For firstStringOnly==true: { 'myStrings' : 'first_String' } (using getFirstString())
For firstStringOnly==false: { 'myStrings' : [ ... ] } (using getMyStrings())
Is this possible to do? I'm specifically looking to avoid using custom serializers, and do this via annotations only.
You can assume a reasonably recent version of Jackson and Java 8.
Just to re-iterate, the question constraints are:
* NO custom serializer
* Both use cases produce the same JSON key
You can generalize getMyStrings() method and make it return Object. And inside check the flag and return first value or all values. Here is my sample
public class tst {
private static class YourObject {
private boolean firstStringOnly;
private List<String> myStrings = new ArrayList<>();
public YourObject(boolean firstStringOnly) {
this.firstStringOnly = firstStringOnly;
this.myStrings.add("str1");
this.myStrings.add("str2");
}
public Object getMyStrings(){
return firstStringOnly ? myStrings.get(0) : myStrings;
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(new YourObject(true)));
System.out.println(mapper.writeValueAsString(new YourObject(false)));
}
}
The output is
{"myStrings":"str1"}
{"myStrings":["str1","str2"]}
EDIT: Sorry, i have misread your initial question. I assume you want to keep both of the typed getters. Would this work for you?
public class TestClass {
private boolean firstStringOnly = true;
private List<String> myStrings = new ArrayList<>();
#JsonIgnore
public boolean isFirstStringOnly() {
return firstStringOnly;
}
public void setFirstStringOnly(boolean firstStringOnly) {
this.firstStringOnly = firstStringOnly;
}
#JsonIgnore
public List<String> getMyStrings() {
return firstStringOnly ? null : myStrings;
}
#JsonIgnore
public String getFirstString() { return firstStringOnly ? myStrings.get(0) : null; }
#JsonProperty("myStrings")
public Object getMyStringsForSerialization() {
return firstStringOnly ? getFirstString() : getMyStrings();
}
public void setMyStrings(List<String> myStrings) {
this.myStrings = myStrings;
}
Currently I have form like below:
public class Form {
private String listOfItems;
public String getListOfItems() {
return listOfItems;
}
public void setListOfItems(String listOfItems) {
this.listOfItems= listOfItems;
}
}
For instanse listOfItems equals to the following string "1,2,3".
The goal is to serialize this form to following format:
{
"listOfItems": [1, 2, 3]
}
It would be good to know how to correctly do such thing? As I know it is possible to create some custom serializer then mark appropriate getter method with it, like this #JsonSerialize(using = SomeCustomSerializer).
But not sure whether it is correct approach, probably any default implementations already exist.
If you can edit your Form class:
public class Form {
private String listOfItems;
public String getListOfItems() {
return listOfItems;
}
public void setListOfItems(String listOfItems) {
this.listOfItems = listOfItems;
}
#JsonProperty("listOfItems")
public List<Integer> getArrayListOfItems() {
if (listOfItems != null) {
List<Integer> items = new ArrayList();
for (String s : listOfItems.split(",")) {
items.add(Integer.parseInt(s)); // May throw NumberFormatException
}
return items;
}
return null;
}
}
By default Jackson looks for getters for serializing. You can override this by using #JsonProperty annotation.
ObjectMapper mapper = new ObjectMapper();
Form form = new Form();
form.setListOfItems("1,2,3");
System.out.print(mapper.writeValueAsString(form));
Outputs:
{"listOfItems":[1,2,3]}
I've got the following problem and I've no clue how to solve this.
I've got a list of different types of points based on a common interface.
I am using Java XStream to marshall and unmarshall those classes.
public static void main(String[] args) {
List<IPoint> listOfPoint = new ArrayList<IPoint>();
listOfPoint.add(new PointTypeA(0.1));
listOfPoint.add(new PointTypeB(0.2));
listOfPoint.add(new PointTypeA(0.3));
PointSet ps = new PointSet(1, listOfPoint);
XStream xstream = new XStream(new StaxDriver());
xstream.processAnnotations(PointTypeA.class);
xstream.processAnnotations(PointTypeB.class);
xstream.processAnnotations(PointSet.class);
String xml = xstream.toXML(ps);
System.out.println(xml);
}
When I print my objects in XML format I get the following result:
<set id="1">
<typeA>
<xCoordinate>0.1</xCoordinate>
</typeA>
<typeB>
<xCoordinate>0.2</xCoordinate>
</typeB>
<typeA>
<xCoordinate>0.3</xCoordinate>
</typeA>
</set>
But instead of the result above, I want to have the following output:
<set id="1">
<typeA>0.1</typeA>
<typeB>0.2</typeB>
<typeA>0.3</typeA>
</set>
What I want is not to have tags like <xCoordinate>, but I want their value to be stored under the classname's tag.
I do not want to ignore the value of xCoordinate field, but I want to to have an "inline value".
Is it possible to do that?
I've tried converters with no success, and I've got no idea how to solve this.
My classes are:
public interface IPoint {
int getSomeInformation();
}
#XStreamAlias("set")
public class PointSet {
#XStreamAsAttribute
private int id;
#XStreamImplicit
private List<IPoint> points;
public PointSet(int id, List<IPoint> points) {
super();
this.id = id;
this.points = points;
}
}
#XStreamAlias("typeA")
public class PointTypeA implements IPoint {
private double xCoordinate;
public PointTypeA(double d) {
super();
this.xCoordinate = d;
}
}
#XStreamAlias("typeB")
public class PointTypeB implements IPoint {
private double xCoordinate;
public PointTypeB(double d) {
super();
this.xCoordinate = d;
}
}
Converter for your point class is rather simple.
public static class CoordConverter implements Converter
{
public boolean canConvert(Class clazz)
{
return PointTypeA.class == clazz;
}
public void marshal(Object object, HierarchicalStreamWriter hsw, MarshallingContext mc)
{
PointTypeA obj = (PointTypeA) object;
hsw.setValue(String.valueOf(obj.xCoordinate));
}
public Object unmarshal(HierarchicalStreamReader hsr, UnmarshallingContext uc)
{
double val = Double.parseDouble(hsr.getValue());
PointTypeA obj = new PointTypeA(val);
return obj;
}
}
You can register it with
xstream.registerConverter(new CoordConverter());
Of course, this converter is valid for PointTypeA class but you can easily extend above code for other classes you need and/or write more generalized version.
I'm using JAXB to generate a xml file from my java objects (xml export), as well as the other way arround (xml import).
In some cases I'm using a "magic-number" to initialize a integer class attribute, because 0 is also valid an I want to initialize the attribute and mark it as "not-yet-edited".
In the xml output generated from JAXB I would be happy if this magic-number is not existing. Is it possible to provide JAXB with something like a mapping information?
Please have a look at the example.
Example:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="my-root")
public class ExampleClass {
/** integer value which represents empty */
public static final int EMPTY_INT = Integer.MAX_VALUE;
/** my id */
#XmlElement(name="id")
private int mMyId = EMPTY_INT;
public void setMyId(int myId) {
mMyId = myId;
}
public int getMyId() {
return mMyId;
}
}
JAXB generates someting like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<my-root>
<id>2147483647</id>
</my-root>
What I want is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<my-root>
<id></id>
</my-root>
I need to tell JAXB to generate "nothing" (see example) if the attribute value is EMPTY_INT and the other way arround (import).
Is that possible somehow?
Or are there other ways to reach that goal?
Thank you for your help.
Steffen
UPDATE:
Based on the answers I tried the following:
Note: The code is shorted (e. g. without imports)
1) add a class: Mydapter
public class MyAdapter extends XmlAdapter<String, Integer> {
#Override
public Integer unmarshal(String val) throws Exception {
System.out.println("Debug1");
return Integer.parseInt(val);
}
#Override
public String marshal(Integer val) throws Exception {
System.out.println("Debug2");
if (val == Integer.MAX_VALUE) {
return "";
} else {
return val.toString();
}
}
}
2) adapted ExampleClass to use "Integer" instead of "int" and annotade it
#XmlJavaTypeAdapter(MyAdapter.class)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="my-root")
public class ExampleClass {
/** integer value which represents empty */
public static final int EMPTY_INT = Integer.MAX_VALUE;
/** my id */
#XmlElement(name="id")
private Integer mMyId = EMPTY_INT;
public void setMyId(int myId) {
mMyId = myId;
}
public int getMyId() {
return mMyId;
}
}
3) Code performing the xml export
public class XMLImportExport {
public static void exportToXml(File xmlFile) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExampleClass.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(new ExampleClass(), xmlFile);
}
}
4) xml output is still
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<my-root>
<id>2147483647</id>
</my-root>
Thank you,
Steffen
Annotate mMyId with #XmlJavaTypeAdapter(YourAdapter.class) and then write an adapter to do the job. Something like this (untested) :
public class YourAdapter extends XmlAdapter<String, Integer> {
public Integer unmarshal(String val) throws Exception {
return Integer.parseInt(val);
}
public String marshal(Integer val) throws Exception {
if ( val == Integer.MAX_VALUE) {
return "";
} else {
return val.toString();
}
}
}
SimpleXML can serialize a Java Enum fine but when it comes to de-serialization, it returns null instead of creating Enum from the generated XML. Is it something I am doing wrong of Enum serialization is not supported at all?
Serialization returns this:
<TestStatus>
<status>Functional</status>
</TestStatus>
Test Enum:
#Root
public enum TestStatus {
AVAILABLE("Functional"),
NOT_AVAILABLE("Dysfunctional");
#Element
private String status;
private Status(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
How do you serialize your enum?
if you use it like this, it should work without problems but will return some different XML:
Example:
#Root
public class Example
{
#Element
private TestStatus status = TestStatus.AVAILABLE;
// ...
}
Test:
final File f = new File("test.xml");
Serializer ser = new Persister();
ser.write(new Example(), f);
Example m = ser.read(Example.class, f);
XML:
<example>
<status>AVAILABLE</status>
</example>
You can rename the xml-tags with annotationarguments, but the value wont be changeable.
Another (possible) solution is using a custom converter:
Annotations of the enum:
#Root()
#Convert(TestStatusConverter.class)
public enum TestStatus
{
// ...
}
Converter (Example)
public class TestStatusConverter implements Converter<TestStatus>
{
#Override
public TestStatus read(InputNode node) throws Exception
{
final String value = node.getNext("status").getValue();
// Decide what enum it is by its value
for( TestStatus ts : TestStatus.values() )
{
if( ts.getStatus().equalsIgnoreCase(value) )
return ts;
}
throw new IllegalArgumentException("No enum available for " + value);
}
#Override
public void write(OutputNode node, TestStatus value) throws Exception
{
// You can customize your xml here (example structure like your xml)
OutputNode child = node.getChild("status");
child.setValue(value.getStatus());
}
}
Test (enum):
final File f = new File("test.xml");
// Note the new Strategy
Serializer ser = new Persister(new AnnotationStrategy());
ser.write(TestStatus.AVAILABLE, f);
TestStatus ts = ser.read(TestStatus.class, f);
System.out.println(ts);
Test (class with enum):
As above but with AnnotationStrategy
You don't need to add annotations to enums, they serialize automatically.