Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XStream: Keep parts of the XML as XML

Tags:

java

xml

xstream

I have the following XML:

<patient>
    <name>Mr. Sick</name>
    <report>
        <paragraph><bold>Conclusion</bold>text...</paragraph>
    </report>
</patient>

I would like to convert this to an instance of class Patient like this:

Class Patient {
    String name = "Mr. Sick";
    String report = "<paragraph><bold>Conclusion</bold>text...</paragraph>";
}

Is it possible to use XStream to convert only part of the XML and keep the report field in XML format? How can it be done?

like image 348
boes Avatar asked Aug 31 '11 10:08

boes


3 Answers

I solved it by creating an Convertor implementation, as explained here. My solution for the problem is as follows:

Patient.java

public class Patient {
    String name;
    Report report;
}

Report.java

public class Report {
    public String report;
}

An implementation of a Convertor for XStream

public class ReportConverter implements Converter { 

    @Override 
    public boolean canConvert(Class classs) { 
        System.out.println("canConvert: " + classs.getName());
        return classs.equals(Report.class); 
    } 

    @Override 
    public void marshal(Object value, HierarchicalStreamWriter writer, 
            MarshallingContext context) {
        // not used in this example
    } 

    // goes recursive through all the nodes in <report>
    String getNodeAsText(HierarchicalStreamReader reader) {
        String result;
        result = "<" + reader.getNodeName() + ">" + reader.getValue();
        while (reader.hasMoreChildren() ) {
            reader.moveDown();
            result += getNodeAsText(reader);
            reader.moveUp();
            result += reader.getValue();
        }
        result += "</" + reader.getNodeName() + ">";
        return result;
    }

    @Override 
    public Object unmarshal(HierarchicalStreamReader reader, 
            UnmarshallingContext context) {
        Report xReport = new Report();
        xReport.report = reader.getValue();
        while (reader.hasMoreChildren() ) {
            reader.moveDown();
            xReport.report += getNodeAsText(reader);
            reader.moveUp();
            xReport.report += reader.getValue();
        }
        return xReport; 
    } 
}

Example use of the convertor with XStream

XStream xStream = new XStream();
xStream.registerConverter(new ReportConverter());
xStream.alias("patient", Patient.class);
xStream.alias("report", Report.class);
String xml = "<patient><name>Mr. Sick</name><report><paragraph>" +
        "some text here<bold>Conclusion</bold>text...</paragraph>" +
        "<sdf>hello world</sdf></report></patient>";
Patient patient = (Patient)xStream.fromXML(xml);
System.out.println("patient.name: " + patient.name);
System.out.println("patient.report: " + patient.report.report);

Output

patient: Mr. Sick
patient.report: <paragraph>some text here<bold>Conclusion</bold>text...
    ...</paragraph><sdf>hello world</sdf>
like image 120
boes Avatar answered Nov 20 '22 11:11

boes


I don't have the answer for the question asked but I suggest an alternative that could maybe help.

You should probably escaping the content of the report field. This will help you avoid invalid xml which can be very problematic at unmarshal time.

Here is one simple way to do that (using the commons libray from apache org.apache.commons.lang.StringEscapeUtils):

class Patient {
   String name = "Mr. Sick";
   String report = StringEscapeUtils.escapeHtml(
         "<paragraph><bold>Conclusion</bold>text...</paragraph>");

  // report = "&lt;paragraph&gt;&lt;bold&gt;Conclusion&lt;/bold&gt;text...&lt;/paragraph&gt;"
}
like image 3
aymeric Avatar answered Nov 20 '22 10:11

aymeric


Better version of getNodeAsText() that includes the attributes of each node:

private String getNodeAsText(HierarchicalStreamReader reader) {

StringBuilder str = new StringBuilder();
str.append("<")
   .append(reader.getNodeName());

for (int i = 0; i < reader.getAttributeCount(); i++) {
  String name = reader.getAttributeName(i);
  String value = reader.getAttribute(i);
  str.append(" ")
     .append(name)
     .append("=\"")
     .append(value)
     .append("\"");
}
str.append(">")
   .append(reader.getValue());

while (reader.hasMoreChildren()) {
  reader.moveDown();
  str.append(getNodeAsText(reader));
  reader.moveUp();
  str.append(reader.getValue());
}
str.append("</")
   .append(reader.getNodeName())
   .append(">");

return str.toString();

}

like image 2
Leo Gomes Avatar answered Nov 20 '22 12:11

Leo Gomes