SimpleXML - Android - CDATA parsing - java

I'm using SimpleXML for parsing XML files in Android. I need to parse the following XML,
<?xml version="1.0" encoding="UTF-8"?>
<Box>
<SerialNumber>XYSSDSD</SerialNumber>
<Alias><![CDATA[SSS: 8]]></Alias>
<BoxType>SD</BoxType>
</Box>
I wrote Bean class to map the above xml
#Element(name="SerialNumber")
private String serialNumber;
#Element(name="Alias", data=true)
private String aliasType;
#Element(name="BoxType")
private String boxType;
I'm getting the following exception while parsing the XML
1-24 23:57:47.407: E/Exception in APItoBEAN(1796): Unable to satisfy
#org.simpleframework.xml.Element(data=true, name=Alias, required=true,
type=void) on field 'aliasType' private .Box.aliasType for class Box
at line 1
Could you anybody help me here?
Thank you for your time!

I tested this on PC (SimpleXML 2.6.6) with following Java Code:
Box Class:
#Root
public class Box
{
#Element(name = "SerialNumber")
private String serialNumber;
#Element(name = "Alias", data = true)
private String aliasType;
#Element(name = "BoxType")
private String boxType;
// ...
}
Reading the XML:
final File f = new File("test.xml"); // your XML is in this file
Serializer ser = new Persister();
Box box = ser.read(Box.class, f);
Works without exception.
Did you clean your project and build it again? Your code seems to be OK.

Related

Spring Boot opencsv

I am trying to use opencsv to parse a csv file like this:
name,purchase,date
TEST,TEST,2020-10-20T00:37:53.562000000Z
TEST,TEST,2020-10-20T00:37:53.562000000Z
I am trying to add the parsed data to a firebase database following this tutorial: https://attacomsian.com/blog/spring-boot-upload-parse-csv-file. This is my class for the data:
public class Records {
#CsvBindByName
private String name;
#CsvBindByName
private String purchase;
#CsvBindByName
private Timestamp date;
// get and setters left out for brevity pls comment if needed
}
When I parse the file I get this error:
Exception in thread "pool-6-thread-2" Exception in thread "pool-6-thread-1" Exception in thread "pool-6-thread-4" Exception in thread "pool-6-thread-3" java.lang.RuntimeException: com.opencsv.exceptions.CsvDataTypeMismatchException: Conversion of 2022-10-20T00:37:53.562000000Z to com.google.cloud.Timestamp failed.
at com.opencsv.bean.concurrent.ProcessCsvLine.run(ProcessCsvLine.java:99)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: com.opencsv.exceptions.CsvDataTypeMismatchException: Conversion of 2022-10-20T00:37:53.562000000Z to com.google.cloud.Timestamp failed.
at com.opencsv.bean.ConverterPrimitiveTypes.convertToRead(ConverterPrimitiveTypes.java:128)
at com.opencsv.bean.BeanFieldSingleValue.convert(BeanFieldSingleValue.java:98)
at com.opencsv.bean.AbstractBeanField.setFieldValue(AbstractBeanField.java:180)
at com.opencsv.bean.AbstractMappingStrategy.setFieldValue(AbstractMappingStrategy.java:581)
at com.opencsv.bean.AbstractMappingStrategy.populateNewBean(AbstractMappingStrategy.java:328)
at com.opencsv.bean.concurrent.ProcessCsvLine.processLine(ProcessCsvLine.java:128)
at com.opencsv.bean.concurrent.ProcessCsvLine.run(ProcessCsvLine.java:83)
... 3 more
Caused by: org.apache.commons.beanutils.ConversionException: Can't convert value '2022-10-20T00:37:53.562000000Z' to type class com.google.cloud.Timestamp
at org.apache.commons.beanutils.converters.AbstractConverter.conversionException(AbstractConverter.java:474)
at org.apache.commons.beanutils.converters.StringConverter.convertToType(StringConverter.java:96)
at org.apache.commons.beanutils.converters.AbstractConverter.convert(AbstractConverter.java:169)
at org.apache.commons.beanutils.converters.ConverterFacade.convert(ConverterFacade.java:61)
at org.apache.commons.beanutils.ConvertUtilsBean.convert(ConvertUtilsBean.java:491)
at com.opencsv.bean.ConverterPrimitiveTypes.convertToRead(ConverterPrimitiveTypes.java:118)
... 9 more
How can i fix this error? Do I need to change the format of the date column? I copied the date format from a record in the database so that format is how it should be stored in the database
I changed the csv format to:
name,purchase,date
TEST,TEST,2018-09-16T08:00:00
TEST,TEST,2018-09-16T08:00:00
I modified the class that binds to the csv to look like this:
public class CsvRecords {
#CsvBindByName
private String name;
#CsvBindByName
private String purchase;
#CsvBindByName
private String date;
// get and setters left out for brevity pls comment if needed
}
The POJO class for the data in db:
public class Records {
private String name;
private String purchase;
private Timestamp date;
// get and setters left out for brevity pls comment if needed
}
When uploading in the controller class I then convert the string to LocalDateTime and then again to Timestamp like this:
#PostMapping("/upload-csv-file")
public String uploadCSVFile(#RequestParam("file") MultipartFile file, Model model) {
// validate file
if (file.isEmpty()) {
model.addAttribute("message", "Please select a CSV file to upload.");
model.addAttribute("status", false);
} else {
// parse CSV file to create a list of `User` objects
try (Reader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
// create csv bean reader
CsvToBean<Records> csvToBean = new CsvToBeanBuilder(reader)
.withType(Records.class)
.withIgnoreLeadingWhiteSpace(true)
.build();
// convert `CsvToBean` object to list of records
List<Records> records = csvToBean.parse();
// save users in DB?
for(int i = 0; i<records.size(); i++){
LocalDateTime localDateTime = LocalDateTime.parse(records.get(i).getDate);
Timestamp timestamp = Timestamp.valueOf(localDateTime);
Records rec = new Records(records.get(i).getName(), records.get(i).getPurchase(), timestamp)
firebaseServices.saveDetails(rec);
}
} catch (Exception ex) {
model.addAttribute("message", "An error occurred while processing the CSV file.");
model.addAttribute("status", false);
}
}
return "file-upload-status";
}
For details on the implementation of the firebaseServices class (saveDetails method) I used this tutorial
Could you have a try in this case that replaces the Timestamp to Date?
like follow this:
#CsvBindByName private Date date;
The timestamp field object is an instance from com.google.cloud.Timestamp package.
As #Allen suggested, you might have to look into java.util.date or event better, the Java 8 time APIs java.time.LocalTime to get support from ConverterPrimitiveTypes to be converted automatically.

XML String Validation with two XSD Strings (with include) / how does LSResourceResolver work?

I am trying to validate an XML String against two Strings that contains an XSD. One XSD includes the other. I get the error:
"Cannot resolve the name 'ServiceSpecificationSchema:ServiceIdentifier' to a(n) 'type definition' component."
It looks like, that my code doesnt recognize the second XSD file. Others solved that problem by using a LSResourceResolver ( seen here: How to validate an XML file using Java with an XSD having an include? )
But in that exampe the files are stored local. Is there a good way, that this method works with my XSD strings?
Any hint would be appreciated.
My code so far:
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema(new SAXSource[]
{
(new SAXSource(new InputSource(new StringReader(XSD)))),
(new SAXSource(new InputSource(new StringReader(XSD2))))
});
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new StringReader(inputXml)));
Finally i found a solution.
This works for me:
#Service
public class ResourceResolverImpl implements LSResourceResolver {
private ILoadFromSRService iLoadFromSRService;
#Autowired
public ResourceResolverImpl(ILoadFromSRService iLoadFromSRService){
this.iLoadFromSRService = iLoadFromSRService;
}
public LSInput resolveResource(String type,
String namespaceURI,
String publicId,
String systemId,
String baseURI) {
String string =iLoadFromSRService.getServiceBaseTypeSchema();
string = string.replace("\n", "").replace("\t", "");
InputStream resourceAsStream = new ByteArrayInputStream( string.getBytes());
return new LSInputImpl(publicId, systemId, resourceAsStream);
}
}

How to format xml instead of original format in springMVC (Restful webservice)

Currently, I want to return the return xml result with XML with the below format :
I tried to use something like this
#XmlRootElement(name = "item")
public class Book implements Serializable {
#XmlAttribute
public int getBookId() {
return bookId;
}
...
....
and
#XmlRootElement(name = "OneBoxResults")
public class JavaClazz {
private List<Book> OneBoxResults;
public List<Book> getOneBoxResults() {
return OneBoxResults;
}
#XmlElements(#XmlElement(name = "book", type = Book.class))
public void setOneBoxResults(List<Book> oneBoxResults) {
OneBoxResults = oneBoxResults;
}
...
However, the return result which I received is only Json format as below :
{"oneBoxResults":[{"bookId":1,"bookName":"Search
Deployment","update":"2014-01-07","description":"A successful deployment
typically involves the following
elements:","path":null},{"bookId":2,"bookName":"GSA
Information","update":"2015-01-07","description":"Configure the OneBox
module so it sends search queries to the provider (a custom
application","path":null}]}
I also attemped to create new format in controller as below :
#RequestMapping(value = "/rest.oneboxSample",produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public #ResponseBody String oneboxSample(){
String tmpOpenField = "<Field name=\"";
String tmpCloseField = "</Field>";
StringBuilder builder = new StringBuilder();
builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
builder.append("<OneBoxResults>").append("<resultCode>");
builder.append("Listbook").append("<resultCode>");
for(int i = 0; i < bookDao.getBooks().size(); i++){
Book tmpBook = bookDao.getBooks().get(i);
builder.append("<MODULE_RESULT>");
builder.append(tmpOpenField).append("bookId\">").append(tmpBook.getBookId()).append(tmpCloseField);
builder.append(tmpOpenField).append("bookName\">").append(tmpBook.getBookName()).append(tmpCloseField);
builder.append(tmpOpenField).append("update\">").append(tmpBook.getUpdate()).append(tmpCloseField);
builder.append(tmpOpenField).append("description\">").append(tmpBook.getDescription()).append(tmpCloseField);
builder.append(tmpOpenField).append("path\">").append(tmpBook.getPath()).append(tmpCloseField);
builder.append("</MODULE_RESULT>");
}
builder.append("</OneBoxResults>");
return builder.toString();
}
But the result is not good. It returned a string instead of xml format which we need.
Now, our system need to receive a xml format instead of an original xml format.
Please tell me know the way to do it .
The below is my source code which I wrote
https://www.dropbox.com/s/4tyg0kp7gkzodod/onebox-service.zip?dl=0
Thanks,

XML Value extraction using Java JAXB

I need a help in the following scenario. I have got a XML String response. In that XML response I need to extract three values. I could not achieve it. I have specified the XML response and the value I need. Also I created set of .java file using pojo one line tool.
My XML Response:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<control>
<status>success</status>
<senderid>XXXXX</senderid>
<controlid>ControlIdHere</controlid>
<uniqueid>false</uniqueid>
<dtdversion>3.0</dtdversion>
</control>
<operation>
<authentication>
<status>success</status>
<userid>XXXXX1</userid>
<companyid>XXXXX</companyid>
<sessiontimestamp>2014-08-22T07:12:37-07:00</sessiontimestamp>
</authentication>
<result>
<status>success</status>
<function>readByQuery</function>
<controlid>testControlId</controlid>
<data listtype="customer" count="100" totalcount="5142" numremaining="5042">
<customer>
<name>A</name>
<id>12</id>
</customer>
<customer>
<name>A</name>
<id>12</id>
</customer>
<customer>
<name>A</name>
<id>12</id>
</customer>
</data>
</result>
</operation>
In this XML response I would need the following values
Like count=100, totalcount=5142
My object class is like this
public class Data {
private String totalcount;
private Sodocument[] sodocument;
private String numremaining;
private String count;
private String listtype;
public String getTotalcount ()
{
return totalcount;
}
public void setTotalcount (String totalcount)
{
this.totalcount = totalcount;
}
public Sodocument[] getSodocument ()
{
return sodocument;
}
public void setSodocument (Sodocument[] sodocument)
{
this.sodocument = sodocument;
}
public String getNumremaining ()
{
return numremaining;
}
public void setNumremaining (String numremaining)
{
this.numremaining = numremaining;
}
public String getCount ()
{
return count;
}
}
XMLInputFactory xif = XMLInputFactory.newFactory();
Reader reader = new StringReader(response.toString());
XMLStreamReader xsr = xif.createXMLStreamReader(reader);
JAXBContext jc = JAXBContext.newInstance(Data.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Data jb = unmarshaller.unmarshal(xsr,Data.class).getValue();
System.out.println(jb.getCount());
This is my JAXB class. For the value getCount gives me null response. Can somebody help me on fixing this?
The generated class is too long for me to paste here but here is what you can try:
1) Use freeformatter.com/xsd-generator.html to generate an xsd from your xml. Copy and paste the generated xsd to a file.
2) Use xjc (comes with jdk installation) to generate the java class. It is a command line tool and you just have to run "xjc generated.xsd" and it will generate the annotated version of your class. The generated file will be for the entire xml response - so your class will really be Response.java, not Data.java and you then can drill into the Data element and it's attributes.
Add these annotations:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Data {
#XmlAttribute
private String totalcount;
#XmlElement
private Sodocument[] sodocument;
#XmlAttribute
private String numremaining;
#XmlAttribute
private String count;
#XmlAttribute
private String listtype;
...
But note that Sodocument[] sodocument (which should be a List!) does not match <customer>. Not sure what the model is - could it be another element as well (depending on listtype!)

How to add <![CDATA[ and ]]> in XML prepared by Jaxb

How to prepare XML with CDATA ,
I am preraring this response via Jaxb,
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<SOAP-ENV:Header/>
<soapenv:Body>
<tem:RequestData>
<tem:requestDocument>
<![CDATA[
<Request>
<Authentication CMId="68" Function="1" Guid="5594FB83-F4D4-431F-B3C5-EA6D7A8BA795" Password="poihg321TR"/>
<Establishment Id="4297867"/>
</Request>
]]>
</tem:requestDocument>
</tem:RequestData>
</soapenv:Body>
</soapenv:Envelope>
But from Jaxb i am not getting CDATA , how to put CDATA inside <tem:requestDocument> element.
Here is my Java Code :
public static String test1() {
try {
initJB();
String response = null;
StringBuffer xmlStr = null;
String strTimeStamp = null;
com.cultagent4.travel_republic.gm.Envelope envelope = null;
com.cultagent4.travel_republic.gm.Header header = null;
com.cultagent4.travel_republic.gm.Body body = null;
com.cultagent4.travel_republic.gm.RequestData requestData = null;
com.cultagent4.travel_republic.gm.RequestDocument requestDocument = null;
com.cultagent4.travel_republic.gm.RequestDocument.Request request = null;
com.cultagent4.travel_republic.gm.RequestDocument.Request.Authentication authentication = null;
com.cultagent4.travel_republic.gm.RequestDocument.Request.Establishment establishment = null;
ObjectFactory objFact = new ObjectFactory();
envelope = objFact.createEnvelope();
header = objFact.createHeader();
envelope.setHeader(header);
body = objFact.createBody();
requestData = objFact.createRequestData();
requestDocument = objFact.createRequestDocument();
request = new RequestDocument.Request();
authentication = new RequestDocument.Request.Authentication();
authentication.setCMId("68");
authentication.setGuid("5594FB83-F4D4-431F-B3C5-EA6D7A8BA795");
authentication.setPassword("poihg321TR");
authentication.setFunction("1");
request.setAuthentication(authentication);
establishment = new RequestDocument.Request.Establishment();
establishment.setId("4297867");
request.setEstablishment(establishment);
requestDocument.setRequest(request);
requestData.setRequestDocument(requestDocument);
body.setRequestData(requestData);
envelope.setBody(body);
jaxbMarshallerForBase = jaxbContextForBase.createMarshaller();
OutputStream os = new ByteArrayOutputStream();
System.out.println();
// output pretty printed
// jaxbMarshallerForBase.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// jaxbMarshallerForBase.marshal(envelope, System.out);
// jaxbMarshallerForBase.marshal(envelope, os);
jaxbMarshallerForBase.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
jaxbMarshallerForBase.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// jaxbMarshallerForBase.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
// get an Apache XMLSerializer configured to generate CDATA
XMLSerializer serializer = getXMLSerializer();
// marshal using the Apache XMLSerializer
SAXResult result = new SAXResult(serializer.asContentHandler());
System.out.println("*************");
jaxbMarshallerForBase.marshal(envelope, result);
System.out.println("--------------");
return null;
} catch (JAXBException ex) {
Logger.getLogger(GM_TravelRepublic.class.getName()).log(Level.SEVERE, null, ex);
} finally {
return null;
}
}
private static XMLSerializer getXMLSerializer() {
// configure an OutputFormat to handle CDATA
OutputFormat of = new OutputFormat();
// specify which of your elements you want to be handled as CDATA.
// The use of the ; '^' between the namespaceURI and the localname
// seems to be an implementation detail of the xerces code.
// When processing xml that doesn't use namespaces, simply omit the
// namespace prefix as shown in the third CDataElement below.
of.setCDataElements(new String[]{"^Request","^Authentication","^Establishment"});
// set any other options you'd like
of.setPreserveSpace(true);
of.setIndenting(true);
StringWriter writer = new StringWriter();
// create the serializer
XMLSerializer serializer = new XMLSerializer(of);
serializer.setOutputByteStream(System.out);
return serializer;
}
Here I am getting same xml , but without CDATA. My server is not accepting the request without CDATA.Please help.
Can you make the logic from this
imports
import org.dom4j.CDATA;
import org.dom4j.DocumentHelper;
sample code
public static String appendCdata(String input) {
CDATA cdata = DocumentHelper.createCDATA(input);
return cdata.asXML();
}
You need to create an custom adapter class which extends the XMLAdapter class.
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class CDATAAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String inStr) throws Exception {
return "<![CDATA[" + inStr + "]]>";
}
#Override
public String unmarshal(String v) throws Exception {
return inStr;
}
}
Inside your Java Bean or POJO define XMLJavaTypeAdapter on the string required in CDATA
#XmlJavaTypeAdapter(value=CDATAAdapter.class)
private String message;
By default, the marshaller implementation of the JAXB RI tries to escape characters. To change this behaviour we write a class that
implements the CharacterEscapeHandler.
This interface has an escape method that needs to be overridden.
import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
m.setProperty("com.sun.xml.internal.bind.characterEscapeHandler",
new CharacterEscapeHandler() {
#Override
public void escape(char[] ch, int start, int length,
boolean isAttVal, Writer writer)
throws IOException {
writer.write(ch, start, length);
}
});
Secondly, it cn also be done via Eclipse MOXy implementation.
CDATA is character data, it looks like your server wants the part of the XML starting with Request to come in as text. It may be enough for you to create an XmlAdapter to convert the instance of Request to a String. The resulting characters will be escaped not in CDATA but this May fit your use case.
Then if you really need it as CDATA in addition to the XmlAdapter you can apply one of the strategies described in the link below:
How to generate CDATA block using JAXB?
From the setCDataElements method description in the Apache docs :
Sets the list of elements for which text node children should be output as CDATA.
What I think that means is, the children of the tem:requestDocument element should all be part of one single text chunk (and not xml elements by themselves) in order for this to work. Once you've done that, probably a simple
of.setCDataElements(new String[]{"tem^requestDocument"});
should do the trick.
Try it and let me know :)
I think that in your private static XMLSerializer getXMLSerializer() method you are setting wrong the CDATA elements, because your CDATA element is <tem:requestDocument> instead of Request Authentication and Establishment which are the content. Try with:
of.setCDataElements(new String[]{"tem^requestDocument","http://tempuri.org/^requestDocument","requestDocument"});
instead of:
of.setCDataElements(new String[]{"^Request","^Authentication","^Establishment"});
Hope this helps,
Your server is expecting <tem:requestDocument> to contain text, and not
a <Request> element. CDATA is really just helpful for creating hand-written
XML so you don't have to worry about escaping embedded XML. The thing is,
JAXB handles escaping just fine and if your server is a good XML citizen it should
treat properly escaped XML the same as XML in a CDATA block.
So, instead of adding a request element inside your requestDocument like
you do in:
requestDocument = objFact.createRequestDocument();
request = new RequestDocument.Request();
...
requestDocument.setRequest(request);
You should first use JAXB to marshal request into a properly escaped String
and set that sa the requestDocument value:
requestDocument = objFact.createRequestDocument();
request = new RequestDocument.Request();
...
String escapedRequest = marshal(request);
requestDocument.setRequest(escapedRequest);
Implementing marshal(request) is left as an exercise. ;)

Categories