Apache Digester XML解析器注释和复合模型

时间:2016-12-26 13:20:59

标签: java xml apache-commons apache-commons-digester

我有一个以下XML文档,我将使用Apache Digester解析器解析为对象模型(通过Digester注释):

<?xml version="1.0" encoding="UTF-8"?>
<Decision>
    <Name>Antivirus software for Windows</Name>
    <Description>Description 1</Description>
    <Url>http://yahoo.com</Url>
    <ImageUrl>http://yahoo.com/img.jpg</ImageUrl>
    <CriterionGroups>
        <CriterionGroup>
            <Name>Windows</Name>
            <Description>Description 1</Description>
            <Criteria>
                <Criterion>
                    <Name>Heuristics</Name>
                    <Description>Description 1</Description>
                </Criterion>
            </Criteria>
        </CriterionGroup>
    </CriterionGroups>
    <Criteria>
        <Criterion>
            <Name>On-demand scan</Name>
            <Description>Description 1</Description>
        </Criterion>
    </Criteria>
    <CharacteristicGroups>
        <CharacteristicGroup>
            <Name>Windows</Name>
            <Description>Description 1</Description>
            <Characteristics>
                <Characteristic>
                    <Name>Country of origin</Name>
                    <Description>Description 1</Description>
                    <ValueType>String</ValueType>
                    <VisualMode>SelectBox</VisualMode>
                    <Sortable>true</Sortable>
                    <Options>
                        <Option>
                            <Value>Shareware</Value>
                            <Description>Description 1</Description>
                        </Option>
                    </Options>
                </Characteristic>
            </Characteristics>
        </CharacteristicGroup>
    </CharacteristicGroups>
    <Characteristics>
        <Characteristic>
            <Name>License</Name>
            <Description>Description 1</Description>
            <ValueType>Integer</ValueType>
            <VisualMode>Slider</VisualMode>
            <Sortable>false</Sortable>
        </Characteristic>
    </Characteristics>
    <Decisions>
        <Decision>
            <Name>Avast Free Antivirus</Name>
            <Description>Description 1</Description>
            <Url>http://google.com</Url>
            <ImageUrl>http://google.com/img.jpg</ImageUrl>
            <Votes>
                <Vote>
                    <CriterionName>On-demand scan</CriterionName>
                    <Weight>4.3</Weight>
                </Vote>
                <Vote>
                    <CriterionName>Heuristics</CriterionName>
                    <CriterionName>On-demand scan</CriterionName>
                    <Weight>4.3</Weight>
                    <Description>Description 1</Description>
                </Vote>
            </Votes>
            <Values>
                <Value>
                    <CharacteristicName>License</CharacteristicName>
                    <Value>Proprietary</Value>
                    <Description>Description 1</Description>
                </Value>
            </Values>
        </Decision>
    </Decisions>
</Decision>

从这个XML可以看出,有两个不同路径的Criterion个节点:

  1. 决策/标准/标准
  2. 决策/ CriterionGroups / CriterionGroup /标准/标准
  3. 这是我的对象模型:

    @ObjectCreate(pattern = "Decision")
    public class DecisionNode {
    
        @BeanPropertySetter(pattern = "Decision/Name")
        private String name;
        @BeanPropertySetter(pattern = "Decision/Description")
        private String description;
        @BeanPropertySetter(pattern = "Decision/Url")
        private String url;
        @BeanPropertySetter(pattern = "Decision/ImageUrl")
        private String imageUrl;
    
        private List<CriterionGroupNode> criterionGroupNodes = new ArrayList<>();
        private List<CriterionNode> criterionNodes = new ArrayList<>();
        private List<CharacteristicGroupNode> characteristicGroupNodes = new ArrayList<>();
        private List<CharacteristicNode> characteristicNodes = new ArrayList<>();
        private List<DecisionNode> decisionNodes = new ArrayList<>();
        private List<VoteNode> voteNodes = new ArrayList<>();
        private List<ValueNode> valueNodes = new ArrayList<>();
    
        ....
    
        @SetNext
        public boolean addCriterionGroupNode(CriterionGroupNode criterionGroupNode) {
            return criterionGroupNodes.add(criterionGroupNode);
        }
    
        ....
    
    }
    
    @ObjectCreate(pattern = "Decision/CriterionGroups/CriterionGroup")
    public class CriterionGroupNode {
    
        @BeanPropertySetter(pattern = "Decision/CriterionGroups/CriterionGroup/Name")
        private String name;
        @BeanPropertySetter(pattern = "Decision/CriterionGroups/CriterionGroup/Description")
        private String description;
    
        private List<CriterionNode> criterionNodes = new ArrayList<>();
    
        ....
    
        @SetNext
        public boolean addCriterionNode(CriterionNode criterionNode) {
            return criterionNodes.add(criterionNode);
        }
    
        ....
    
    }
    
    @ObjectCreate(pattern = "Decision/Criteria/Criterion")
    public class CriterionNode {
    
        @BeanPropertySetter(pattern = "Decision/Criteria/Criterion/Name")
        private String name;
        @BeanPropertySetter(pattern = "Decision/Criteria/Criterion/Description")
        private String description;
    
        public CriterionNode() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
    }
    

    现在我只能解析Decision/Criteria/Criterion,但Decision/CriterionGroups/CriterionGroup/Criteria/Criterion仍然是NULL。如何配置我的模型并更改注释,以便能够在两个不同的位置解析CriterionNode

    另外,我不明白为什么解析器会在Criterion找到两个Decision/Criteria/Criterion个节点而不是一个节点:

    enter image description here

1 个答案:

答案 0 :(得分:2)

我可以看到两个问题:

首先,您发布的代码仅匹配作为决策的直接子项的Criteria。即,您已经匹配了#34;决策/标准/标准&#34;,但没有&#34;决策/标准组/标准组/标准/标准&#34;,因此永远不会创建更深层的元素。最简单的解决方案就是使用通配符:

@ObjectCreate(pattern = "*/Criteria/Criterion")
public static class CriterionNode {

  @BeanPropertySetter(pattern = "*/Criteria/Criterion/Name")
  private String name;
  @BeanPropertySetter(pattern = "*/Criteria/Criterion/Description")
  private String description;

第二个问题是SetNext的{​​{1}}规则,这个问题有点棘手。为了减少追逐,我认为这段代码适合你:

CriterionNode

这是有效的原因是注释构建下一组规则的方式。

set next rule需要三件事:

  1. 模式。
  2. 方法名称。
  3. 参数类型。
  4. 所以这个注释试图实现的是等同于:

    @ObjectCreate(pattern = "Decision")
    public class DecisionNode {
    
      ...
    
      @SetNext
      public boolean addCriterionNode(CriterionNode criterionNode) {
        return criterionNodes.add(criterionNode);
      }
    
    }
    
    @ObjectCreate(pattern = "Decision/CriterionGroups/CriterionGroup")
    public class CriterionGroupNode {
    
      ...
    
      // no SetNext rule on this method
      public boolean addCriterionNode(CriterionNode criterionNode) {
        return criterionNodes.add(criterionNode);
      }
    
    }
    

    请注意,此规则中的任何位置都不会提及拥有digester.addSetNext("*/Criteria/Criterion", "addCriterionNode", "CriterionNode") DecisionNode

    方法名称和参数类型很简单 - 它们只是直接来自带注释的方法 - 但模式不太清楚。注释处理查看与参数匹配的注释以推断模式,因此在这种情况下,参数为CriterionGroupNode,并且具有匹配的CriterionNode注释&#34; * / Criteria / Criterion& #34;,所以它创建了所需的规则。

    您在ObjectCreate类中不需要第二个SetNextRule的原因是它会复制完全相同的处理,因此会添加重复的规则。

    关于消化器注释的注意事项

    我将关于Digester注释添加我的标准免责声明:我个人不喜欢它们并且从不使用它们有两个原因:

    1. 一个更通用:我认为注释应该只用在哪里 他们对课程本身说了些什么,而不是如何使用它,但这只是我个人对过度使用注释的看法。
    2. 专门针对Digester注释:它们只能满足极其简单的情况,即使是像这样容易的事情也会导致像上面那样的问题。
    3. 我发现基于规则的配置对于快速映射是最简单的,如果需要扩展它也是最强大的。在这种情况下,例如:

      CriterionGroupNode

      请注意,仅需要BeanPropertySetterRule的扩展版本,因为您的XML实体不遵循Java Bean约定(该属性必须是较低的驼峰 - 因此getName和setName定义属性&#34; name&#34; not& #34;名称&#34)。因此,如果您的XML使用小写实体,例如&#34; name&#34;和&#34;描述&#34;然后你可以使用更短的:

      RulesModule rules = new AbstractRulesModule() {
        @Override
        public void configure() {
      
          forPattern("Decision")
              .createObject().ofType(DecisionNode.class);
      
          forPattern("Decision/Name").addRule(new BeanPropertySetterRule("name"));
          forPattern("Decision/Description").addRule(new BeanPropertySetterRule("description"));
          forPattern("Decision/Url").addRule(new BeanPropertySetterRule("url"));
          forPattern("Decision/ImageUrl").addRule(new BeanPropertySetterRule("imageUrl"));
      
          forPattern("Decision/CriterionGroups/CriterionGroup")
              .createObject().ofType(CriterionGroupNode.class)
              .then().setNext("addCriterionGroupNode");
      
          forPattern("Decision/CriterionGroups/CriterionGroup/Name").addRule(new BeanPropertySetterRule("name"));
          forPattern("Decision/CriterionGroups/CriterionGroup/Description").addRule(new BeanPropertySetterRule("description"));
      
          forPattern("*/Criterion")
              .createObject().ofType(CriterionNode.class)
              .then().setNext("addCriterionNode");
      
          forPattern("*/Criterion/Name").addRule(new BeanPropertySetterRule("name"));
          forPattern("*/Criterion/Description").addRule(new BeanPropertySetterRule("description"));
      
        }
      };
      
      DigesterLoader loader = DigesterLoader.newLoader(rules);
      Digester digester = loader.newDigester();
      
      DecisionNode dn = digester.parse(...);
      

      您的XML绝对没有理由遵循Java Bean惯例 - 如果您想知道为什么扩展版本是必要的,我只是指出这一点。

      干杯