SimpleXML framework: element with element or text - java

I must to parse XML which can have two forms:
<Command><Variable/></Command>
or:
<Command>some text</Command>
How can I do this? When I try to declare both #Element and #Text in class responsible for Command parsing then exception is thrown when I try to parse XML to instance of this class.
My current version of code:
#Root(name = "Command", strict = false)
public class AppCommand {
#Element(name = "Variable", required = false)
#Getter
private Variable variable;
#Text(required = false)
#Getter
private String content;
}
And exception is: Text annotation #org.simpleframework.xml.Text(required=false, empty=, data=false) on field 'content' private java.lang.String com.example.AppCommand.content used with elements in class com.example.AppCommand

My solution (not beautiful, but works and doesn't require much work to implement):
private static class SerializerWithPreprocessor extends Persister {
public SerializerWithPreprocessor(RegistryMatcher matcher, Format format) {
super(matcher, format);
}
#Override
public <T> T read(Class<? extends T> type, String source) throws Exception {
source = source.replaceFirst("<Command (.*)>([[\\w||[+=]]&&[^<>]]+)</Command>", "<Command $1><Content>$2</Content></Command>");
return super.read(type, source);
}
}
So I just created new Serializer class. This class use regular expressions to change Text element inside Command into normal Element. Then I can use:
#Root(name = "Command", strict = false)
public class AppCommand {
#Element(name = "Variable", required = false)
#Getter
private Variable variable;
#Element(name = "Content", required = false)
#Getter
private String content;
}
and during deserialization everything works like I wanted to.

Yes, Simple can't deal with this.
Command.java:
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Text;
#Root
public class Command {
#Element(required = false, name = "Variable")
private Variable variable;
#Text(required = false)
private String text;
}
Variable.java:
class Variable {
}
SOPlayground.java:
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
public class SOPlayground {
public static void main(String[] args) throws Exception {
Serializer serializer = new Persister();
String xml1 = "<Command><Variable/></Command>";
String xml2 = "<Command>some text</Command>";
serializer.validate(Command.class, xml1);
serializer.validate(Command.class, xml2);
}
}
This does compile but it does not run:
Exception in thread "main" org.simpleframework.xml.core.TextException: Text annotation #org.simpleframework.xml.Text(data=false, required=false, empty=) on field 'text' private java.lang.String de.lhorn.so.Command.text used with elements in class de.lhorn.so.Command
It looks like can not have both #Element and #Text members.

Related

Use JAXB to create Object from XML String with Multiple Groups in XML file

I need to convert XML string into java object.
This is the XML File
<?xml version="1.0" encoding="UTF-8"?>
<DATA_DS>
<G_1>
<TERM_ID>4</TERM_ID><NAME>30 Net</NAME>
</G_1>
</DATA_DS>
I have created Class like this;
#XmlRootElement(name = "DATA_DS")
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymentTerm {
#XmlElement(name = "TERM_ID")
private double termId;
#XmlElement(name = "NAME")
private String termName;
public double getTermId() {
return termId;
}
public void setTermId(double termId) {
this.termId = termId;
}
public String getTermName() {
return termName;
}
public void setTermName(String termName) {
this.termName = termName;
}
}
In Main Class
jaxbContext = JAXBContext.newInstance(PaymentTerm.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
PaymentTerm paymentTerm = (PaymentTerm) unmarshaller.unmarshal(reader);
This doesn't unmarshell the XML string properly because of nested groups in XML file.
If I remove the G_1 group from XML file then it convert perfectly. I need to do conversion with G_1 group
Where I have to fix the code?
<DATA_DS> contains one element, <G_1>, which itself contains two elements, <TERM_ID> and <NAME>, so your objects needs to reflect that, i.e. the class representing <DATA_DS> must have one field, typed to be a class representing <G_1>, which must have two fields.
Where I have to fix the code?
You need to create a class for <G_1>:
#XmlRootElement(name = "DATA_DS")
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymentTerm {
#XmlElement(name = "G_1", required = true)
private PaymentGroup group;
}
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymentGroup {
#XmlElement(name = "TERM_ID", required = true)
private double termId;
#XmlElement(name = "NAME", required = true)
private String termName;
}
You should also consider why <G_1> exists, e.g. can there be more than one <G_1> inside <DATA_DS>? If so, make it a list:
#XmlElement(name = "G_1", required = true)
private List<PaymentGroup> groups;

Missing name, in state: START_OBJECT parsing XML using Jackson

I'm trying to parse some XML that looks like this:
<correlationMatrix>
<assetMatrix numAssets="45">
<correlations asset="Name1" />
<correlations asset="Name2">
<correlation asset="Name3">1.23</correlation>
</correlations>
<correlations asset="Name4">
<correlation asset="Name5">2.34</correlation>
<correlation asset="Name6">3.45</correlation>
</correlations>
</assetMatrix>
</correlationMatrix>
I've created 3 classes:
#JsonIgnoreProperties(ignoreUnknown = true)
public class CorrelationMatrix {
private List<Correlations> assetMatrix;
public List<Correlations> getAssetMatrix() {
return assetMatrix;
}
public void setAssetMatrix(List<Correlations> assetMatrix) {
this.assetMatrix = assetMatrix;
}
}
And
#JsonIgnoreProperties(ignoreUnknown = true)
public class Correlations {
private String asset;
private List<Correlation> correlation;
public String getAsset() {
return asset;
}
public void setAsset(String asset) {
this.asset = asset;
}
public List<Correlation> getCorrelation() {
return correlation;
}
public void setCorrelations(List<Correlation> correlation) {
this.correlation = correlation;
}
}
Then finally
#JsonIgnoreProperties(ignoreUnknown = true)
public class Correlation {
}
As you can see I've removed everything from the final inner class, but it still fails to parse. I've tried removing <correlations asset="Name1" /> from the input but that's not the source of the problem. If I remove private List<Correlation> correlation; from Correlations then that does then parse successfully but obviously doesn't have the information I need.
What is it that I need to do differently here to parse what is essentially a 2 dimensional array from XML into Java using Jackson (2.2.0 if that matters)?
The error I get is:
Missing name, in state: START_OBJECT (through reference chain: CorrelationMatrix["assetMatrix"]->Correlations["correlation"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(
Update:
The problem seems to be associated with the values inside correlation. If I remove 1.23, 2.34 and 3.45 from my example data then it parses - so I need to somehow tell Jackson how to map them.
I was able to parse all the elements in the example xml with these modified classes (add getters, setters and use correct name setCorrelation in Correlations):
class CorrelationMatrix {
private AssetMatrix assetMatrix;
}
class AssetMatrix {
#JacksonXmlProperty(isAttribute = true)
private int numAssets;
#JacksonXmlElementWrapper(useWrapping = false)
private List<Correlations> correlations;
}
class Correlations {
#JacksonXmlProperty(isAttribute = true)
private String asset;
#JacksonXmlElementWrapper(useWrapping = false)
private List<Correlation> correlation;
}
class Correlation {
#JacksonXmlProperty(isAttribute = true)
private String asset;
#JacksonXmlText
private double correlation;
}
I didn't need #JsonIgnoreProperties(ignoreUnknown = true) anywhere
#JacksonXmlProperty(isAttribute = true) is needed for attributes like asset and numAssets
There are 2 types of lists in the xml that are both unwrapped so specify it with this #JacksonXmlElementWrapper(useWrapping = false)
You can parse the innermost double numbers with this #JacksonXmlText although the field in Java is not text.
I introduced a wrapper class AssetMatrix to capture numAssets

SimpleXMLConverter parse xml nodes

I'm stuck. How to parse Node with same name child node?
In this example i need nodes with rate attribute.
<xml>
<Rates>
<Rates winrate_cap="9999">
<Rates rate="323"/>
<Rates rate="343"/>
<Rates rate="2338"/>
<Rates rate="233"/>
</Rates>
</Rates>
</xml>
My response wrapper class:
#Root(name = "xml", strict = false)
public class XMLResponse {
#ElementList(entry = "Rates")
public List<Rates> response;
public static class Rates {
#Attribute(name = "winrate_cap", required = false)
public String winrate_cup;
#ElementList(required = false, entry = "Rates")
public List<Rates> rates;
}
public static class Rates {
#Attribute(name = "rate", required = false)
public String rate;
}
}
You are on the right way. As you have many Rates here, better name the class more with more context and set the xml name via Annotation.
I've separated the XMLResponse and Rates classes, but this doesn't make any difference.
The class XMLRespone maps the <xml>...</xml> part:
#Root(name = "xml")
public class XMLRespone
{
#Element(name = "Rates")
private Rates rates;
// ...
}
All types for <Rates...>...</Rates> is done by Rates class (and it's inner classes, which map the structure:
#Root(name = "Rates")
public class Rates
{
#Element(name = "Rates")
private RateList rates;
// ...
public static class RateList
{
#ElementList(entry = "Rates", inline = true)
private ArrayList<RateValue> values;
#Attribute(name = "winrate_cap", required = true)
private int winrateCap;
// ...
}
public static class RateValue
{
#Attribute(name = "rate", required = true)
private int rate;
// ...
}
}
Deserializing your XML gives this output (using generated toString()):
XMLRespone{rates=Rates{rates=RateList{values=[RateValue{rate=323}, RateValue{rate=343}, RateValue{rate=2338}, RateValue{rate=233}], winrateCap=9999}}}

Jackson XML - deserializing empty classes and polymorphism

I have the following interface:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(value = EmptyProxy.class, name = "empty"),
... other types not included ...
})
public interface Proxy {
}
I have the following implementation:
#JsonTypeName("empty")
public static class EmptyProxy implements Proxy {
}
As you can see, it is just an empty class. I left the other (working) implementations out of this example.
I have the following container data class:
public static class Data {
#JacksonXmlProperty(localName = "name")
private String name;
#JacksonXmlProperty(localName = "proxy")
private Proxy proxy;
}
Deserializing EmptyProxy does not seem to work. For example:
final ObjectMapper mapper = new XmlMapper().registerModule(new JacksonXmlModule());
final Data data = mapper.readValue("<data><name>my-name</name><proxy><empty/></proxy></data>", Data.class);
This gives the following exeption:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Test$EmptyProxy out of VALUE_NULL token
at [Source: java.io.StringReader#59ec2012; line: 1, column: 42] (through reference chain: Data["proxy"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857)
Is this a bug in Jackson? FWIW, when I add a dummy field to EmptyProxy, it works.
update
I tried with JAXB only, and get the same result. Code:
public static class Data {
#XmlElement(name = "name")
private String name;
#XmlElements({
#XmlElement(type = EmptyProxy.class, name = "empty")
})
private Proxy proxy;
}
public interface Proxy {
}
#XmlType(name = "empty")
public static class EmptyProxy implements Proxy {
}
public static void main(String[] a) throws IOException {
final ObjectMapper mapper = new XmlMapper()/*.registerModule(new JacksonXmlModule())*/.registerModule(new JaxbAnnotationModule());
final Data data = mapper.readValue("<data><name>my-name</name><proxy><empty></empty></proxy></data>", Data.class);
}
I have created a bug entry for this. See http://github.com/FasterXML/jackson-dataformat-xml/issues/169.

XML to java Object conversion using simpleframework

I am converting xml file to java object using simpleframework. I am using generics since my xml's internal nodes gets changing. But simpleframework throws an error while converting. Here is my sample code:
#Root(name = "searchresult", strict = false)
public class ResponseVO<T>
{
#ElementList(entry = "document", inline = true)
public List<T> elementVOList = new ArrayList<T>();
public List<T> getElementsVOList()
{
return elementVOList;
}
public void setElementsVOList(List<T>list)
{
elementVOList = list;
}
}
ResponseVO is container for various other VOs as below:
#Root(name = "document", strict = false)
public class Projects_Display_VO
{
#Element(name = "projectname")
private String projectName;
#Attribute(name = "id")
private int tmpid;
public int getTmpid()
{
return tmpid;
}
public void setTmpid(int tmpid)
{
this.tmpid = tmpid;
}
/**
* ProjectId
*/
#Element(name = "projectid")
private String projectID;
public String getProjectName()
{
return projectName;
}
public void setProjectName(String projectName)
{
this.projectName = projectName;
}
public int getProjectID()
{
return Integer.parseInt(projectID);
}
public void setProjectID(String projectID)
{
this.projectID = projectID;
}
}
And the XML file is as below:
<searchresult>
<query>id:(PROJ2 PROJ6)</query>
<document id="0">
<projectid>2</projectid>
<projectname>Redundant Demo Project</projectname>
<doctype>Projects</doctype>
<summary>||Demo Project</summary>
<title>Redundant Demo Project</title>
</document>
<document id="1">
<projectid>6</projectid>
<projectname>Redundant Demo Project2</projectname>
<doctype>Projects</doctype>
<summary>||Main terminal links.</summary>
<title>Terminal 5 Project</title>
</document>
</searchresult>
The code for conversion is as bellow:
ResponseVO<Projects_Display_VO> resp = (ResponseVO<Projects_Display_VO>) SerializationUtil.deserialize(ResponseVO.class, reader);
Here i am using serializer from simpleframework. But it throws following error
Exception::Attribute 'id' does not have a match in class java.lang.Object at line 5
org.simpleframework.xml.core.AttributeException: Attribute 'id' does not have a match in class java.lang.Object at line 5
at org.simpleframework.xml.core.Composite.readAttribute(Composite.java:555)
at org.simpleframework.xml.core.Composite.readAttributes(Composite.java:474)
at org.simpleframework.xml.core.Composite.readSection(Composite.java:387)
I dont know whats going wrong here.A it works fine without generics.
Thanks in advance
This is caused by erasure, T is not available at runtime. Java does not allow it.

Categories