Java XStream:在解析时折叠XML层次结构

Java XStream:在解析时折叠XML层次结构,java,xml,parsing,converter,xstream,Java,Xml,Parsing,Converter,Xstream,我有一个XML文档(由Adobe XFA forms生成),其中包含以下数据: <Position> <PositionBorder> <Title/> <StartDate/> <EndDate/> </PositionBorder> </Position> 由于此文件是在其他地方定义的,因此我无权更改获得的XML格式。 在Java代码中,我创建了一个

我有一个XML文档(由Adobe XFA forms生成),其中包含以下数据:

<Position>
   <PositionBorder>
       <Title/>
       <StartDate/>
       <EndDate/>
   </PositionBorder>
</Position>

由于此文件是在其他地方定义的,因此我无权更改获得的XML格式。

在Java代码中,我创建了一个包含标题、开始和结束日期的Position类

我的问题是,当我使用XStream解析文件时,它需要一个PositionBorder类来保存标题和日期。我想基本上忽略边框,将所有字段放入Position类中

我真正想做的是使用ConvertOther方法来转换position元素的子元素。我试图这样做,但失败了,因为我的PositionConverter被调用为PositionBorder(当我调用ConvertOther时)


有人知道如何在解析XML时处理XML结构的崩溃吗?

使用自定义转换器并不十分困难。这是一个有点长的示例,但我希望它足够简单,可以获得您需要做的事情的要点:

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public final class ConverterTest {
    public static void main(String[] args) {
        XStream xstream = new XStream();
        xstream.autodetectAnnotations(true);
        xstream.registerConverter(new PositionConverter());

        final Position position = new Position();
        position.setTitle("The Title");
        position.setStartDate("The Start Date");
        position.setEndDate("The End Date");

        final String xml = xstream.toXML(position);
        System.out.println("Generated XML:");
        System.out.println(xml);

        final Position genPosition = (Position) xstream.fromXML(xml);
        System.out.println("Generated Position:");
        System.out.println("\tTitle: " + genPosition.getTitle());
        System.out.println("\tStart Date: " + genPosition.getStartDate());
        System.out.println("\tEnd Date: " + genPosition.getEndDate());
    }

    @XStreamAlias("Position")
    private static class Position {
        public String getEndDate() {
            return endDate;
        }

        public void setEndDate(String endDate) {
            this.endDate = endDate;
        }

        public String getStartDate() {
            return startDate;
        }

        public void setStartDate(String startDate) {
            this.startDate = startDate;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        private String title;
        private String startDate;
        private String endDate;
    }

    private static class PositionConverter implements Converter {
        public boolean canConvert(Class clazz) {
            return Position.class == clazz;
        }

        public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
            Position position = (Position)value;
            writer.startNode("PositionBorder");

            writer.startNode("Title");
            writer.setValue(position.getTitle());
            writer.endNode();

            writer.startNode("StartDate");
            writer.setValue(position.getStartDate());
            writer.endNode();

            writer.startNode("EndDate");
            writer.setValue(position.getEndDate());
            writer.endNode();

            writer.endNode();
        }

        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
            Position position = new Position();
            // move it to <PositionBorder> tag.
            reader.moveDown();
            // now move it to <Title> tag.
            reader.moveDown();
            String title = reader.getValue();
            position.setTitle(title);
            reader.moveUp(); // moves back to <PositionBorder>

            reader.moveDown(); // should move down to <StartDate> tag
            String startDate = reader.getValue();
            position.setStartDate(startDate);
            reader.moveUp(); // move back to <PositionBorder>

            reader.moveDown(); // should move down to <EndDate> tag
            String endDate = reader.getValue();
            position.setEndDate(endDate);
            reader.moveUp(); // move back to <PositionBorder>


            return position;
        }
    }
}
import com.thoughtworks.xstream.xstream;
导入com.thoughtworks.xstream.annotations.XStreamAlias;
导入com.thoughtworks.xstream.converters.Converter;
导入com.thoughtworks.xstream.converters.MarshallingContext;
导入com.thoughtworks.xstream.converters.UnmarshallingContext;
导入com.thoughtworks.xstream.io.HierarchycalStreamReader;
导入com.thoughtworks.xstream.io.HierarchycalStreamWriter;
公共最终类转换器测试{
公共静态void main(字符串[]args){
XStream XStream=新的XStream();
xstream.autodetectanotations(true);
寄存器转换器(新的位置转换器());
最终位置=新位置();
职位。职位名称(“职位”);
位置。设置开始日期(“开始日期”);
位置。setEndDate(“结束日期”);
最后一个字符串xml=xstream.toXML(位置);
System.out.println(“生成的XML:”);
System.out.println(xml);
最终位置genPosition=(位置)xstream.fromXML(xml);
System.out.println(“生成的位置:”);
System.out.println(“\tTitle:+genPosition.getTitle());
System.out.println(“\t开始日期:+genPosition.getStartDate());
System.out.println(“\tEnd Date:+genPosition.getEndDate());
}
@XStreamAlias(“职位”)
私有静态类位置{
公共字符串getEndDate(){
返回结束日期;
}
公共无效setEndDate(字符串endDate){
this.endDate=endDate;
}
公共字符串getStartDate(){
返回起始日期;
}
公共无效设置开始日期(字符串开始日期){
this.startDate=startDate;
}
公共字符串getTitle(){
返回标题;
}
公共无效集合标题(字符串标题){
this.title=标题;
}
私有字符串标题;
私有字符串起始日期;
私有字符串结束日期;
}
私有静态类PositionConverter实现转换器{
公共布尔canConvert(类clazz){
返回位置.class==clazz;
}
公共无效封送处理(对象值、HierarchycalStreamWriter编写器、封送上下文){
位置=(位置)值;
作者:startNode(“位置边界”);
作者:startNode(“标题”);
writer.setValue(position.getTitle());
writer.endNode();
作者:startNode(“StartDate”);
writer.setValue(position.getStartDate());
writer.endNode();
作者:开始日期(“结束日期”);
writer.setValue(position.getEndDate());
writer.endNode();
writer.endNode();
}
公共对象解组(HierarchycalStreamReader读取器,解组上下文){
位置=新位置();
//将其移动到标记。
reader.moveDown();
//现在将其移动到标记。
reader.moveDown();
字符串标题=reader.getValue();
职位.职称(职称);
reader.moveUp();//移回
reader.moveDown();//应向下移动到标记
字符串startDate=reader.getValue();
位置。设置开始日期(开始日期);
reader.moveUp();//移回
reader.moveDown();//应向下移动到标记
字符串endDate=reader.getValue();
position.setEndDate(endDate);
reader.moveUp();//移回
返回位置;
}
}
}
试着运行一下,看看会发生什么。当然,您需要修改它以适应您自己的类型——我只是在Position的所有字段中使用了字符串(我相信您的Position类也不是嵌套的),但是从字符串转换为日期(或其他类型)应该非常简单

您需要注意的一件事是匹配reader.moveDown()和reader.moveUp()调用。(而且,如果您要执行任何编组而不是仅仅解编组(我不希望您的问题会这样),您还需要匹配writer.startNode()和writer.endNode()调用。)这可能不会对本示例造成任何问题,但我确信,如果您正在做任何更大的事情,或者使用同一个XStream或Converter实例处理多个文件,那么它将引发问题。另外,如果您从无效位置尝试reader.moveDown(),您将得到一个非常糟糕的异常——这应该是非常明显的


我必须稍微使用一下moveUp/moveDown方法才能将它们放在正确的位置,因此我确信您需要对其进行测试和调整,直到获得所需的内容。

我发现这种方法更易于使用:

@Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        Position mPosition = new Position();
        while (reader.hasMoreChildren()) {

            reader.moveDown();

            String nodeName = reader.getNodeName();

            if ("Title".equalsIgnoreCase(nodeName)) {
                mPosition.setTitle(reader.getValue());
            } else if ("StartDate".equalsIgnoreCase(nodeName)) {
                mPosition.setStartDate(reader.getValue());
            }else if ("attributeexample".equalsIgnoreCase(nodeName)) {
                mPosition.setAttributeExample(reader.getAttribute("attrname"));
            }

            reader.moveUp();
        }

        return mPosition;
    }
你必须使用Xstrea吗