Reading XML with JDOMXpath - java

I have the follwing XML file -
<?xml version="1.0" encoding="UTF-8"?>
<BatchOrders xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<BatchHeader>
<ServiceProvider>123456789</ServiceProvider>
</BatchHeader>
<OrderDetails>
<MessageType>HelloWorld</MessageType>
<IssueDateTime>22/01/2012 00:00:00</IssueDateTime>
<receivedDateTime>22/01/2012 00:00:00</receivedDateTime>
<Status>TestStatus</Status>
</OrderDetails>
</BatchOrders>
I want to read in the contents and set them to fields I have created. So I have the following code below (not some is omitted - I have just included what I think I need to show. The below is in a test class which I have created - I also have a writer as part of this class that writes an XML File fine to disk as I expect. The problem I am facing is reading the file above and displaying the contents read to the Console just for now.
File myFileRead = null;
FileReader myFileReader = null;
try {
myFileRead = new File("C:/Path/myfile.xml");
myRecord = new myRecord();
myFileReader = new FileReader(myFileRead);
myXPathReader reader = new myXPathReader(myFileReader);
while (reader.hasNext())
{
record = reader.next();
//prints out then to cosole
}
So from above I have the myRecord class where I have the getters/setters for e.g ServiceProvider, etc. I also then have a class for myXpathReader which does the following:
private Document document;
private List batchorders;
private Iterator iterator;
public myXPathReader (Reader myFileReader)
throws Exception
{
SAXBuilder builder = new SAXBuilder();
document = builder.build(myFileReader);
batchorders = new JDOMXPath("//BatchOrders").selectNodes(document);
iterator = batchorders.iterator();
}
public int getSize() { return batchorders.size(); }
public boolean hasNext() { return iterator.hasNext(); }
public myRecord next()
throws Exception {
Element element = (Element) iterator.next();
myRecord record = new myRecord();
record.setServiceProvider((new JDOMXPath("./ServiceProvider").stringValueOf(element)));
//Some more sets ans close class etc...
Now if I debug the code and after the element on iterator.next I can see the file contents have being read in correctly. But on my console the ServiceProvider value and in fact all the values are getting set to empty string "". Am I doing something incorrect on the JDOMXPath in order to pull the value from the XML?

In your example XML ServiceProvider is not a child of BatchOrders, there's another level (BatchHeader) in between. So your second XPath expression should probably be
BatchHeader/ServiceProvider
instead of ./ServiceProvider

Related

How the value of HashMap changes?

I parse some xml files using XStream libraries. The result is a map for every file. When I debug, the result map is what I was looking for, but when I go to next line the value of map changes out of the blue! However it doesn't go for the next round in the "for" loop, it contains the information of next file. What can cause it?
public class Debug {
public String path = "E:\\Projects\\svn\\FANRPProduction\\WMS\\src\\main\\resources\\wms\\bpmn\\bp";
public XStream xStream;
public Map<String, List<CallActivity>> bpTpMap;
public void findBPandTP() throws IOException {
File root = new File(path);
File[] xmlFiles = FindAllXMLFiles.recursive(root);
bpTpMap=new HashMap<String, List<CallActivity>>();
for (File xml : xmlFiles) {
if (xml != null) {
xStream = new XStream(new StaxDriver());
xStream.alias("definitions", CallActivity.class);
xStream.registerConverter(new CallActivityConverter());
bpTpMap = (Map) xStream.fromXML(xml);//Here I get correct information. For example "WMS_RBP_OutgoingWeighing"
List<String> bpList = new ArrayList<String>(bpTpMap.keySet()); //Here I see the name of the next file in the path in bpTpMap, which is "WMS_BP_WeighingConfiguration"
}
}
}
}
I would suggest you to debug with the following:
bpTpMap = (Map) xStream.fromXML(xml);//Here I get correct information. For example "WMS_RBP_OutgoingWeighing"
System.out.println(bpTpMap.size());
Set<String> setOfKeys = bpTpMap.keySet();
System.out.println("Initial value of keys:"+bpTpMap);//Here you would see any extra values if the map has it

Supercsv - unable to find method exception

I have the below implementation.
csvReader = new CsvBeanReader(new InputStreamReader(stream), CsvPreference.STANDARD_PREFERENCE);
lastReadIdentity = (T) csvReader.read(Packages.class, Packages.COLS);
In my Packages.class
I have set my unitcount variable.
public String getUnitCount() {
return unitCount;
}
public void setUnitCount(String unitCount) {
this.unitCount = unitCount;
}
This works fine when it is taken as a string, but when taken as a integer, it throws the below exception. Please help
private int unitCount;
public int getUnitCount() {
return unitCount;
}
public void setUnitCount(int unitCount) {
this.unitCount = unitCount;
}
Exception:
org.supercsv.exception.SuperCsvReflectionException: unable to find method setUnitCount(java.lang.String) in class com.directv.sms.data.SubscriberPackages - check that the corresponding nameMapping element matches the field name in the bean, and the cell processor returns a type compatible with the field
context=null
at org.supercsv.util.ReflectionUtils.findSetter(ReflectionUtils.java:139)
at org.supercsv.util.MethodCache.getSetMethod(MethodCache.java:95)
I'm not sure about SuperCsv, but univocity-parsers should be able to handle this without a hitch, not to mention it is at least 3 times faster to parse your input.
Just annotate your class:
public class SubscriberPackages {
#Parsed(defaultNullRead = "0") // if the file contains nulls, then they will be converted to 0.
private int unitCount; // The attribute name will be matched against the column header in the file automatically.
}
To parse the CSV into beans:
// BeanListProcessor converts each parsed row to an instance of a given class, then stores each instance into a list.
BeanListProcessor<SubscriberPackages> rowProcessor = new BeanListProcessor<SubscriberPackages>(SubscriberPackages.class);
CsvParserSettings parserSettings = new CsvParserSettings(); //many options here, check the tutorial.
parserSettings.setRowProcessor(rowProcessor); //uses the bean processor to handle your input rows
parserSettings.setHeaderExtractionEnabled(true); // extracts header names from the input file.
CsvParser parser = new CsvParser(parserSettings); //creates a parser with your settings.
parser.parse(new FileReader(new File("/path/to/file.csv"))); //all rows parsed here go straight to the bean processor
// The BeanListProcessor provides a list of objects extracted from the input.
List<SubscriberPackages> beans = rowProcessor.getBeans();
Disclosure: I am the author of this library. It's open-source and free (Apache V2.0 license).

JaxB marshaler overwriting file contents

I am trying to use JaxB to marshall objects I create to an XML. What I want is to create a list then print it to the file, then create a new list and print it to the same file but everytime I do it over writes the first. I want the final XML file to look like I only had 1 big list of objects. I would do this but there are so many that I quickly max my heap size.
So, my main creates a bunch of threads each of which iterate through a list of objects it receives and calls create_Log on each object. Once it is finished it calls printToFile which is where it marshalls the list to the file.
public class LogThread implements Runnable {
//private Thread myThread;
private Log_Message message = null;
private LinkedList<Log_Message> lmList = null;
LogServer Log = null;
private String Username = null;
public LogThread(LinkedList<Log_Message> lmList){
this.lmList = lmList;
}
public void run(){
//System.out.println("thread running");
LogServer Log = new LogServer();
//create iterator for list
final ListIterator<Log_Message> listIterator = lmList.listIterator();
while(listIterator.hasNext()){
message = listIterator.next();
CountTrans.addTransNumber(message.TransactionNumber);
Username = message.input[2];
Log.create_Log(message.input, message.TransactionNumber, message.Message, message.CMD);
}
Log.printToFile();
init_LogServer.threadCount--;
init_LogServer.doneList();
init_LogServer.doneUser();
System.out.println("Thread "+ Thread.currentThread().getId() +" Completed user: "+ Username+"... Number of Users Complete: " + init_LogServer.getUsersComplete());
//Thread.interrupt();
}
}
The above calls the below function create_Log to build a new object I generated from the XSD I was given (SystemEventType,QuoteServerType...etc). These objects are all added to an ArrayList using the function below and attached to the Root object. Once the LogThread loop is finished it calls the printToFile which takes the list from the Root object and marshalls it to the file... overwriting what was already there. How can I add it to the same file without over writing and without creating one master list in the heap?
public class LogServer {
public log Root = null;
public static String fileName = "LogFile.xml";
public static File XMLfile = new File(fileName);
public LogServer(){
this.Root = new log();
}
//output LogFile.xml
public synchronized void printToFile(){
System.out.println("Printing XML");
//write to xml file
try {
init_LogServer.marshaller.marshal(Root,XMLfile);
} catch (JAXBException e) {
e.printStackTrace();
}
System.out.println("Done Printing XML");
}
private BigDecimal ConvertStringtoBD(String input){
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setGroupingSeparator(',');
symbols.setDecimalSeparator('.');
String pattern = "#,##0.0#";
DecimalFormat decimalFormat = new DecimalFormat(pattern, symbols);
decimalFormat.setParseBigDecimal(true);
// parse the string
BigDecimal bigDecimal = new BigDecimal("0");
try {
bigDecimal = (BigDecimal) decimalFormat.parse(input);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bigDecimal;
}
public QuoteServerType Log_Quote(String[] input, int TransactionNumber){
BigDecimal quote = ConvertStringtoBD(input[4]);
BigInteger TransNumber = BigInteger.valueOf(TransactionNumber);
BigInteger ServerTimeStamp = new BigInteger(input[6]);
Date date = new Date();
long timestamp = date.getTime();
ObjectFactory factory = new ObjectFactory();
QuoteServerType quoteCall = factory.createQuoteServerType();
quoteCall.setTimestamp(timestamp);
quoteCall.setServer(input[8]);
quoteCall.setTransactionNum(TransNumber);
quoteCall.setPrice(quote);
quoteCall.setStockSymbol(input[3]);
quoteCall.setUsername(input[2]);
quoteCall.setQuoteServerTime(ServerTimeStamp);
quoteCall.setCryptokey(input[7]);
return quoteCall;
}
public SystemEventType Log_SystemEvent(String[] input, int TransactionNumber, CommandType CMD){
BigInteger TransNumber = BigInteger.valueOf(TransactionNumber);
Date date = new Date();
long timestamp = date.getTime();
ObjectFactory factory = new ObjectFactory();
SystemEventType SysEvent = factory.createSystemEventType();
SysEvent.setTimestamp(timestamp);
SysEvent.setServer(input[8]);
SysEvent.setTransactionNum(TransNumber);
SysEvent.setCommand(CMD);
SysEvent.setFilename(fileName);
return SysEvent;
}
public void create_Log(String[] input, int TransactionNumber, String Message, CommandType Command){
switch(Command.toString()){
case "QUOTE": //Quote_Log
QuoteServerType quote_QuoteType = Log_Quote(input,TransactionNumber);
Root.getUserCommandOrQuoteServerOrAccountTransaction().add(quote_QuoteType);
break;
case "QUOTE_CACHED":
SystemEventType Quote_Cached_SysType = Log_SystemEvent(input, TransactionNumber, CommandType.QUOTE);
Root.getUserCommandOrQuoteServerOrAccountTransaction().add(Quote_Cached_SysType);
break;
}
}
EDIT: The below is code how the objects are added to the ArrayList
public List<Object> getUserCommandOrQuoteServerOrAccountTransaction() {
if (userCommandOrQuoteServerOrAccountTransaction == null) {
userCommandOrQuoteServerOrAccountTransaction = new ArrayList<Object>();
}
return this.userCommandOrQuoteServerOrAccountTransaction;
}
Jaxb is about mapping java object tree to xml document or vice versa. So in principle, you need complete object model before you can save it to xml.
Of course it would not be possible, for very large data, for example DB dump, so jaxb allows marshalling object tree in fragments, letting the user control moment of the object creation and marshaling. Typical use case would be fetching records from DB one by one and marshaling them one by one to a file, so there would not be problem with the heap.
However, you are asking about appending one object tree to another (one fresh in memory, second one already represented in a xml file). Which is not normally possible as it is not really appending but crating new object tree that contains content of the both (there is only one document root element, not two).
So what you could do,
is to create new xml representation with manually initiated root
element,
copy the existing xml content to the new xml either using XMLStreamWriter/XMLStreamReader read/write operations or unmarshaling
the log objects and marshaling them one by one.
marshal your log objects into the same xml stram
complete the xml with the root closing element. -
Vaguely, something like that:
XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(new FileOutputStream(...), StandardCharsets.UTF_8.name());
//"mannually" output the beginign of the xml document == its declaration and the root element
writer.writeStartDocument();
writer.writeStartElement("YOUR_ROOT_ELM");
Marshaller mar = ...
mar.setProperty(Marshaller.JAXB_FRAGMENT, true); //instructs jaxb to output only objects not the whole xml document
PartialUnmarshaler existing = ...; //allows reading one by one xml content from existin file,
while (existing.hasNext()) {
YourObject obj = existing.next();
mar.marshal(obj, writer);
writer.flush();
}
List<YourObject> toAppend = ...
for (YourObject toAppend) {
mar.marshal(obj,writer);
writer.flush();
}
//finishing the document, closing the root element
writer.writeEndElement();
writer.writeEndDocument();
Reading the objects one by one from large xml file, and complete implementation of PartialUnmarshaler is described in this answer:
https://stackoverflow.com/a/9260039/4483840
That is the 'elegant' solution.
Less elegant is to have your threads write their logs list to individual files and the append them yourself. You only need to read and copy the header of the first file, then copy all its content apart from the last closing tag, copy the content of the other files ignoring the document openkng and closing tag, output the closing tag.
If your marshaller is set to marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
each opening/closing tag will be in different line, so the ugly hack is to
copy all the lines from 3rd to one before last, then output the closing tag.
It is ugly hack, cause it is sensitive to your output format (if you for examle change your container root element). But faster to implement than full Jaxb solution.

converting xml to java objects with xstream in java

I have an xml ans i want to make it objects , i am using xsteam for this and I have added xstream jars in my classpath..
below is my xml...
<Eurexflows xmlns:eur="http://www.eurexchange.com/EurexIRSFullInventoryReport" xmlns:fpml="http://www.fpml.org/FpML-5/confirmation">
<EurexMessageObject>
<CCPTradeId>109599</CCPTradeId>
<novDateTime>2012-02-15 10:59:00.0</novDateTime>
</EurexMessageObject>
<EurexMessageObject>
<CCPTradeId>122270</CCPTradeId>
<novDateTime>2012-06-29 18:59:00.0</novDateTime>
</EurexMessageObject>
</Eurexflows>
below is my pojo...
public class EurexMessageObject {
private Long CCPTradeId;
private String migratedDate;
public Long getCCPTradeId() {
return CCPTradeId;
}
public void setCCPTradeId(Long cCPTradeId) {
CCPTradeId = cCPTradeId;
}
public String getMigratedDate() {
return migratedDate;
}
public void setMigratedDate(String migratedDate) {
this.migratedDate = migratedDate;
}
}
and in my main class I have coded this way..
String xmlInputtra="C:\\Rahul\\InputXml\\Xmloutput.xml";
try
{
// get XStream instance and set required aliases
XStream xstream = new XStream();
xstream.alias("EurexMessageObject", com.rbos.gdspc.eurex.EurexMessageObject.class);
// prepare cash flow message from xslt output
EurexMessageObject eurexflowMsg = (EurexMessageObject) xstream.fromXML(xmlInputtra);
System.out.println(eurexflowMsg.toString());
}catch(Exception e)
{
e.printStackTrace();
}
now upon debuging I am getting the following exception..please advise how can I overcome from this
com.thoughtworks.xstream.io.StreamException: : only whitespace content allowed before start tag and not C (position: START_DOCUMENT seen C... #1:1)
Well,the thing that is overlooked here is how you are reading in the XML file.you are using the method fromXML which is expecting the actual XML input and not the file name. So when it parses your xml (which is "Xmloutput.xml" not the actual xml)
I suggest you to use a FileReader/BufferedReader in order to get the contents of the XML back. Something like this should work:
XStream instream = new XStream();
BufferedReader br = new BufferedReader(new FileReader("Xmloutput.xml"));
StringBuffer buff = new StringBuffer();
String line;
while((line = br.readLine()) != null){
buff.append(line);
}
EurexMessageObject eurexflowMsg = (EurexMessageObject)instream.fromXML(buff.toString());
I hope it will help you, best regards.
Here path for XML file:
String xmlInputtra="C:\\Rahul\\InputXml\\Xmloutput.xml";
is treated as XML contents,
so you need to pass as String for that you can read file and pass to constructor.

Deserializing an XML element containing other xml markup as a single string using SimpleXML

I have been using SimpleXML for a while now to serialize my java objects, but
I am still learning and run into trouble sometimes. I have the following XML that
I want to deserialize:
<messages>
<message>
<text>
A communications error has occurred. Please try again, or contact administrator. Alternatively, please register.
</text>
</message>
I would like process it such that the contents of the element are treated as a single string and the anchor tags to be ignored. I have no control on how this XML is generated - it is, as you can see, an error message from some server. How do I achieve this?
Many thanks in advance.
You might want to try escaping the text by importing:
import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
And using it as:
a.setWordCloudStringToDisplay(escapeHtml(wordcloud));
To read text and Element is not offered basically by Simple XML. You have to use Converter. You can read https://stackoverflow.com/questions/17462970/simpleframwork-xml-element-with-inner-text-and-child-elements that answer quite the same problem except that it read only one text.
Here is a solution to get multiples text and href in a single string.
First, I create a A class for the 'a' tag, with a toString methode to print the tag as it is in xml :
#Root(name = "a")
public class A {
#Attribute(required = false)
private String href;
#Text
private String value;
#Override
public String toString(){
return "" + value + "";
}
}
Then the Text class to read the 'text', where the convert is necessary :
#Root(name = "Text")
#Convert(Text.Parsing.class)
public class Text {
#Element
public String value;
private static class Parsing implements Converter<Text> {
// to read <a href...>
private final Serializer ser = new Persister();
#Override
public Text read(InputNode node) throws Exception {
Text t = new Text();
String s;
InputNode aref;
// read the begining of text (until first xml tag)
s = node.getValue();
if (s != null) { t.value = s; }
// read first tag (return null if no more tag in the Text)
aref = node.getNext();
while (aref != null) {
// add to the value using toString() of A class
t.value = t.value + ser.read(A.class, aref);
// read the next part of text (after the xml tag, until the next tag)
s = node.getValue();
// add to the value
if (s != null) { t.value = t.value + s; }
// read the next tag and loop
aref = node.getNext();
}
return t;
}
#Override
public void write(OutputNode node, Text value) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
}
}
Note that I read the 'a' tag with a standard serializer, and add in the A class a toString methode to get it back as an xml string. I have not found a way to read directly the 'a' tag as text.
And the main class (don't forget the AnnotationStrategy which map the Convert method to the deserialisation of the text element) :
public class parseText {
public static void main(String[] args) throws Exception {
Serializer serializer = new Persister(new AnnotationStrategy());
InputStream in = ClassLoader.getSystemResourceAsStream("file.xml");
Text t = serializer.read(Text.class, in, false);
System.out.println("Texte : " + t.value);
}
}
When I use it with the following xml file :
<text>
A communications error has occurred. Please try again, or contact administrator.
Alternatively, please register.
</text>
It give the following result :
Texte :
A communications error has occurred. Please try again, or contact administrator.
Alternatively, please register.
I hope this will help you to solve your problem.

Categories

Resources