【问题标题】:How to generate unique element names or auto-increment element names with JAXB?如何使用 JAXB 生成唯一元素名称或自增元素名称?
【发布时间】:2014-05-30 13:11:40
【问题描述】:

当使用 JAXB 编组到 XML 时,有没有办法自动增加 Collection 的元素名称?我使用以下代码生成 XML:

public void saveTestSettings(final String filename, final DefaultTestSettings t) throws Exception
    {
        try
        {

            final Path path = FileSystems.getDefault().getPath((paths.getTestSettingsPath() + filename));
            if (Files.notExists(path.getParent()))
                throw new Exception(path.getParent().toString() + " does not exist!!");
            final OutputStream out = new BufferedOutputStream(Files.newOutputStream(path));
            final JAXBContext jaxbContext = JAXBContext.newInstance(DefaultTestSettings.class);
            final Marshaller m = jaxbContext.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.marshal(t, out);
            out.flush();
            out.close();
        }
        catch (JAXBException | IOException e)
        {
            e.printStackTrace();
            throw new Exception(e.getMessage());
        }
    }

生成的 XML 如下所示:

   <defaultTestSettings>
    <groups>
        <instruments>
            <name>A200</name>
        </instruments>
        <name>A0</name>
       </groups>
    <groups>
        <instruments>
            <name>A300</name>
        </instruments>
        <instruments>
            <name>A400</name>
        </instruments>
        <name>A1</name>
       </groups>
    </defaultTestSettings>

我希望 groupsinstruments 自动递增,结果如下所示:

<defaultTestSettings>
    <groups1>
        <instruments1>
            <name>A200</name>
        </instruments1>
        <name>A0</name>
    </groups1>
    <groups2>
        <instruments1>
            <name>A300</name>
        </instruments1>
        <instruments2>
            <name>A400</name>
        </instruments2>
        <name>A1</name>
    </groups2>
</defaultTestSettings>

我对使用 XML 架构不太熟悉,这样行吗?

【问题讨论】:

  • 一个有用的契约,但不是列表或数组,不,除了为整个元素组件指定元素名称之外,您没有任何控制权。
  • 我建议不要使用自动递增的元素名称。您是否有理由要以这种方式实现它?
  • 我同意,但是对解析 XML 文件的其他软件有依赖性。元素必须是唯一的,才能区分各个元素。不幸的是,我无法控制。

标签: java xml jaxb


【解决方案1】:

以下是可以处理此用例的几种方法:

选项 #1 - 使用标准 JAXB API

我在下面的回答扩展了excellent answer given by Ilya。我已将其扩展为使用 XmlAdaptergetElements() 方法中删除逻辑。

Java 模型

默认设置

import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class DefaultSettings {

    private List<Groups> groups = new ArrayList<Groups>();

    @XmlAnyElement
    @XmlJavaTypeAdapter(GroupsAdapter.class)
    public List<Groups> getGroups() {
        return groups;
    }

}

群组

import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

public class Groups {

    private List<Instruments> instruments = new ArrayList<Instruments>();

    @XmlAnyElement
    @XmlJavaTypeAdapter(InstrumentsAdapter.class)
    public List<Instruments> getInstruments() {
        return instruments;
    }

}

仪器

public class Instruments {

}

XmlAdapter

GroupsAdapter

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;

public class GroupsAdapter extends XmlAdapter<JAXBElement<Groups>, Groups> {

    private int counter = 1;
    private InstrumentsAdapter instrumentsAdapter = new InstrumentsAdapter();

    public InstrumentsAdapter getInstrumentsAdapter() {
        return instrumentsAdapter;
    }

    @Override
    public Groups unmarshal(JAXBElement<Groups> v) throws Exception {
        return v.getValue();
    }

    @Override
    public JAXBElement<Groups> marshal(Groups v) throws Exception {
        instrumentsAdapter.resetCounter();
        return new JAXBElement<Groups>(new QName("groups" + counter++), Groups.class, v);
    }

}

仪器适配器

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;

public class InstrumentsAdapter extends XmlAdapter<JAXBElement<Instruments>, Instruments> {

    private int counter = 1;

    @Override
    public Instruments unmarshal(JAXBElement<Instruments> v) throws Exception {
        return v.getValue();
    }

    @Override
    public JAXBElement<Instruments> marshal(Instruments v) throws Exception {
        return new JAXBElement<Instruments>(new QName("instruments" + counter++), Instruments.class, v);
    }

    public void resetCounter() {
        counter = 1;
    }

}

演示代码

演示

import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(DefaultSettings.class, Groups.class, Instruments.class);

        Groups groups1 = new Groups();
        groups1.getInstruments().add(new Instruments());
        groups1.getInstruments().add(new Instruments());

        Groups groups2 = new Groups();
        groups2.getInstruments().add(new Instruments());
        groups2.getInstruments().add(new Instruments());

        DefaultSettings ds = new DefaultSettings();
        ds.getGroups().add(groups1);
        ds.getGroups().add(groups2);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        GroupsAdapter groupsAdapter = new GroupsAdapter();
        marshaller.setAdapter(groupsAdapter);
        marshaller.setAdapter(groupsAdapter.getInstrumentsAdapter());
        marshaller.marshal(ds, System.out);
    }

}

输出

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<defaultSettings>
    <groups1>
        <instruments1/>
        <instruments2/>
    </groups1>
    <groups2>
        <instruments1/>
        <instruments2/>
    </groups2>
</defaultSettings>

选项 #2 - 使用 EclipseLink JAXB (MOXy) 的 @XmlVariableNode 扩展

以下是如何使用我们在 EclipseLink JAXB (MOXy) 中添加的 hte @XmlVariableNode 扩展来映射此用例的链接:

【讨论】:

  • 我对这两个答案都投了赞成票,因为它们让我走上了正确的道路,但是这个详细的解释非常有帮助,因为它简单明了。谢谢!
【解决方案2】:

您可以使用@XmlAnyElement 注释来做到这一点。
简单示例groups 标签。

@XmlRootElement(name = "defaultTestSettings")
@XmlSeeAlso(Group.class)
public class DefaultTestSettings
{
   private static int counter = 1;
   private static final String PROP = "group";

   List<Group> groups = new ArrayList<Group>();

   @XmlAnyElement
   public List<JAXBElement<Group>> getElements()
   {
      final  List<JAXBElement<Group>> retVal = new ArrayList<>();
      for (final Group g : groups)
      {
         retVal.add(new JAXBElement(new QName(PROP + counter++), Group.class, g));
      }
      return retVal;
   }
}

【讨论】:

    猜你喜欢
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多