Byte array is not working in JAXB classes - java

I am trying to use byte array like this (JAXB class). However, I am getting all 0s in the msg field even though I pass valid characters. The "id" and "myid" fields are parsed successfully and it is failing for the byte array field.
#XmlRootElement(name = "testMessage")
#XmlAccessorType(XmlAccessType.FIELD)
public class TestMessage
{
#XmlAttribute
private Integer id;
#XmlElement(name = "myid")
private Long myid;
#XmlElement(name = "msg")
private byte[] msg;
}

Using JAXB of Java 1.6.0_23 i get the following xml file for a TestMessage instance:
TestMessage testMessage = new TestMessage();
testMessage.id = 1;
testMessage.myid = 2l;
testMessage.msg = "Test12345678".getBytes();
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<testMessage id="1">
<myid>2</myid>
<msg>VGVzdDEyMzQ1Njc4</msg>
</testMessage>
If you unmarshall this xml content you should get back the TestMessage instance including the msg byte array (which is base64 encoded in the xml file).

You can use xml adapters for your byte array xml element. As you now, every element get marshalling/unmarshalling and adapters are use for situations such as converting date time with specified format, type convertions etc. while marshalling/unmarshalling.
HexBinaryAdapter class is one of those adapters belongs to javax.xml.bind.annotation.adapters so you can use it.
public class TestMessage {
#XmlAttribute
private Integer id;
#XmlElement(name = "myid")
private Long myid;
#XmlJavaTypeAdapter(HexBinaryAdapter.class)
#XmlElement(name = "msg")
private byte[] msg;
}
Yet, if you prefer a custom convertion, you can create your own adapter for converting bytes for a specified format such as base64 etc.
To do that you must write your own unmarshalling/marshalling methods,
public final class MyAdapter extends XmlAdapter<String, byte[]> {
public byte[] unmarshal(String s) {
if (s == null)
return null;
return decode()); // your way to decode.
}
public String marshal(byte[] bytes) {
if (bytes == null)
return null;
return encode(); //your way to encode
}
}
then you give your marshaller/unmarshaller in #XmlJavaTypeAdapter anotation ;
public class TestMessage {
#XmlAttribute
private Integer id;
#XmlElement(name = "myid")
private Long myid;
#XmlJavaTypeAdapter(MyAdapter.class)
#XmlElement(name = "msg")
private byte[] msg;
}

Related

XML parser (Unmarshall) using JaxB to get elemet from xml file

I new to jaxB XML parser. i need to get the all the attribute (idref, type, name, scope) from element tag ** I tried something but getting error.
XML File
<?xml version="1.0" encoding="windows-1252"?>
<xmi:XMIxmi:version="2.1"xmlns:uml="http://www.omg.org/spec/UML/20090901"xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" xmlns:thecustomprofile="http://www.sparxsystems.com/profiles/thecustomprofile/1.0" xmlns:SysML="http://www.omg.org/spec/SysML/20161101/SysML">
<xmi:Documentation exporter="Enterprise Architect" exporterVersion="6.5" exporterID="1555"/>
<xmi:Extension extender="Enterprise Architect" extenderID="6.5">
<elements>
<element xmi:idref="EAPK_5560E5AF_736A_4703_AC79_CA3FAA60984B" xmi:type="uml:Package" name="PackageView" scope="public"></element>
<element xmi:idref="EAPK_59058493_9220_4b05_888A_67C6854C97EC" xmi:type="uml:Package" name="Read from Communication Interface" scope="public">
</element>
<element xmi:idref="EAID_870B8E54_0FF2_4a90_A9C1_23F477DF695F" xmi:type="uml:Activity" name="Read from communication interface" scope="public">
</element>
</elements>
</xmi:Extension>
</xmi:XMI>
Main Class
public class XmlElement {
public static void main(String[] args) {
JAXBContext jaxbContext;
String fileLocation = "C:\\vinoth\\XML_JaxbParser\\elements.xml";
try (FileInputStream fileInputStream = new FileInputStream(fileLocation)) {
System.out.println("******** PARSING START ********");
jaxbContext = JAXBContext.newInstance(Xmi.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
elements elements = (elements) unmarshaller.unmarshal(fileInputStream);
System.out.println(elements);
System.out.println("******** PARSING DONE ********");
} catch (Exception e) {
System.out.println("******** ERROR: SOMETHING WENT WRONG ********");
e.printStackTrace();
}
}
}
elements.java
#XmlRootElement(name = "xmi")
#XmlAccessorType(XmlAccessType.FIELD)
public class Elements {
#XmlElement(name = "elements")
private List<Elements> elements;
// Generate Getters and Setters...
#Override
public String toString() {
return "Elements [elements="+ elements +"]";
}
}
element.java
#XmlAccessorType(XmlAccessType.FIELD)
public class Element {
#XmlAttribute(name = "idref")
private String idref;
#XmlAttribute(name = "type")
private String type;
#XmlAttribute(name = "name")
private String name;
#XmlAttribute(name = "scope")
private String scope;
//Generate Getters and Setters...
#Override
public String toString() {
return "Element [idref=" + idref + ",type=" + type + ", name=" + name + ", scope=" + scope+ "]";
}
}
I need to get the the element attribute from the given XML file. I don't know where I made a mistake. Root element and parent and children, i can understand mistake from mapping section. but I couldn't sort out.
Your XML file uses many different XML namespaces,
which you need to consider in your Java code.
Especially notice, that the namespace URIs (e.g.
"http://schema.omg.org/spec/XMI/2.1") are the
only relevant thing. The namespace prefixes (e.g. xmi:) are not
relevant for Java. They were only invented to make the XML
easier to read for humans.
I recommend to define all your XML namespace URIs as Java
string contants to avoid typing them many times in your code:
public class NameSpaces {
public final static String UML = "http://www.omg.org/spec/UML/20090901";
public final static String XMI = "http://schema.omg.org/spec/XMI/2.1";
public final static String THE_CUSTOM_PROFILE = "http://www.sparxsystems.com/profiles/thecustomprofile/1.0";
public final static String SYSML = "http://www.omg.org/spec/SysML/20161101/SysML";
}
You need to carefully look at your XML content (XML elements
XMI, Documentation, Extension, Elements, Element and
their XML attributes) to see which XML namespace is used where.
Then in all your Java classes you need to specify the right
namespace in the #XmlAttribute and #XmlElement annotations.
You will also need to specify a namespace in the #XmlRootElement
annotation of your root class.
See also the Javadoc of the JAXB annotations.
I will not do all your work for you, but instead only show how
your root class and the Element class may look like.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "XMI", namespace = NameSpaces.XMI)
public class XMI {
#XmlAttribute(name = "version", namespace = NameSpaces.XMI)
private String version;
#XmlElement(name = "Documentation", namespace = NameSpaces.XMI)
private Documentation documentation;
#XmlElement(name = "Extension", namespace = NameSpaces.XMI)
private Extension extension;
// Getters, setters, toString omitted here for brevity
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Element {
#XmlAttribute(name = "idref", namespace = NameSpaces.XMI)
private String idref;
#XmlAttribute(name = "type", namespace = NameSpaces.XMI)
private String type;
#XmlAttribute(name = "name")
private String name;
#XmlAttribute(name = "scope")
private String scope;
// Getters, setters, toString omitted here for brevity
}

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

Object mapping fields

I have an objects class A:
public class A {
private Long id;
private String name;
private String mail;
private String moreData;
// ...
}
class B:
public class B {
private Long id;
private String name;
private String crc;
// ...
}
Can I use jackson to provide field mapping from object A to B copying correspond fields into target object.
I need from object
A {
Long id = 23L;
String name = "name";
String mail = "mail";
String moreData = "moreData";
// ...
}
get as target object
B {
Long id = 23L;
String name = "name";
String crc = mull;
// ...
}
after object mapping processing...
Is it possible implement solution using com.fasterxml.jackson in simple way?
Sure you can. Not withstanding a full understanding of why you want to do this, or that I think there might be more efficient ways than converting to JSON then back, but if you would like to use Jackson, here is what I would do:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
B b = objectMapper.readValue(objectMapper.writeValueAsString(a), B.class);
Hope this helps. should do the job. The key will be to tell Jackson to not fail on unknown properties so it drops those you are not sure of.

Simple framework. Do not serialize some variables to xml

I have problem serializing java object to XML. My classes are shown below:
#Root(strict = false, name = "Detail")
public class ProductList {
#ElementList(inline = true, entry = "Product")
private List<Product> products;
}
#Root(strict = false)
public class Product {
#Element(name = "ProductCode")
private String productCode;
#Element(name = "ProductPrice")
private double productPrice;
#Element(name = "Currency")
private String currency;
#Element(name = "ConversionRate")
private int conversionRate;
#Element(name = "ProductPoints", required = false)
private int productPoints;
#Element(name = "ProductCount", required = false)
private int productCount;
#Element(name = "ProductName", required = false)
private String productName;
#Element(name = "MinPrice", required = false)
private double minPricet;
#Element(name = "MaxPrice", required = false)
private double maxPricet;
#Element(name = "CanChangePrice", required = false)
private String canChangePrice;
}
The XML below is sent from server and it's deserialized without any problem:
<?xml version="1.0" encoding="UTF-8"?>
<Detail>
<Product>
<ProductCode>0001</ProductCode>
<ProductPrice>0.90</ProductPrice>
<Currency>GEL</Currency>
<ConversionRate>200</ConversionRate>
<ProductName>Bread</ProductName>
<MinPrice>0.9</MinPrice>
<MaxPrice>0.9</MaxPrice>
<CanChangePrice>N</CanChangePrice>
</Product>
<Product>
...
</Product>
</Detail>
I try to generate the XML document that will have this structure:
<?xml version="1.0" encoding="UTF-8"?>
<Detail>
<Product>
<ProductCode>0001</ProductCode>
<ProductPrice>0.90</ProductPrice>
<Currency>GEL</Currency>
<ConversionRate>200</ConversionRate>
<ProductPoints>180</ProductPoints>
<ProductCount>1</ProductCount>
</Product>
<Product>
...
</Product>
</Detail>
But I get this:
<Detail>
<Product>
<ProductCode>0001</ProductCode>
<ProductPrice>0.9</ProductPrice>
<Currency>GEL</Currency>
<ConversionRate>200</ConversionRate>
<productPoints>180</productPoints>
<ProductCount>1</ProductCount>
<ProductName>Bread</ProductName>
<MinPrice>0.9</MinPrice>
<MaxPrice>0.9</MaxPrice>
<CanChangePrice>N</CanChangePrice>
</Product>
<Product>
...
</Product>
</Detail>
Tags <ProductName>, <MinPrice>, <MaxPrice>, <CanChangePrice> mustn't be included in the serialized XML.
Is there any way I can tell the framework not to include specific tags\variables while serializing?
The problem
Your class members are not initialized to null and so required=false has not the effect of not serializing them.
ints are serialized by org.simpleframework.xml.transform.IntegerTransform. The write(Integer) method of this class is simple:
public String write(Integer value) {
return value.toString();
}
As you can see, simple autoboxing is used.
Your primitive int is initialized to 0 by the constructor.
It is boxed into an Integer.
The String value of this Integer is "0" which is not null.
Solution
Use Integer, not int for your class members.
Edit
If you don't want to serialize a member, don't annotate it with #Element. Simple is not about 'generating' XML but about mapping instances to/from XML. Every member you want to map needs an annotation. Every member with an annotation will be mapped.
My solution is pretty ugly. I created another class for XML serialization, that contains every field from the original class except the fields I wanted to omit in the XML.
You can override default serialization mechanism with converter to customize the output. For example that you provide it will be:
public class CustomProductConverter implements Converter<Product> {
private void createProductPropertyNode(OutputNode productNode, String propertyName, String value) throws Exception {
productNode.getChild(propertyName).setValue(value);
}
private void createProductPropertyNode(OutputNode productNode, String propertyName, int value) throws Exception {
createProductPropertyNode(productNode, propertyName, String.valueOf(value));
}
private void createProductPropertyNode(OutputNode productNode, String propertyName, double value) throws Exception {
createProductPropertyNode(productNode, propertyName, String.valueOf(value));
}
#Override
public Product read(InputNode inputNode) throws Exception {
throw new UnsupportedOperationException();
}
#Override
public void write(OutputNode outputNode, Product product) throws Exception {
createProductPropertyNode(outputNode, "ProductCode" , product.getProductCode());
createProductPropertyNode(outputNode, "ProductPrice" , product.getProductPrice());
createProductPropertyNode(outputNode, "Currency" , product.getCurrency());
createProductPropertyNode(outputNode, "ConversionRate", product.getConversionRate());
createProductPropertyNode(outputNode, "ProductPoints" , product.getProductPoints());
createProductPropertyNode(outputNode, "ProductCount" , product.getProductCount());
outputNode.commit();
}
}
And then use a RegistryStrategy for serialization:
Registry registry = new Registry();
registry.bind(Product.class, CustomProductConverter.class);
Strategy strategy = new RegistryStrategy(registry);
Serializer serializer = new Persister(strategy);
// serialize your object with serializer
PROS: you can dynamically serialize same object with different converters and get different output without modification of the model classes.
CONS: in the case of a complex model there will be a lot of similar code in converter.

Categories