java - 使用混合类型时编码 XML 没有子级

标签 java xml jaxb

我正在尝试解码 DICOM Sequence VR type它作为 XML CLOB 存储在数据库中。根据标准,序列可以包含属性以及子序列。可供引用的示例是 Content Sequence属性。

因此,我在 JAXB DTO 中使用 @XmlMixed 来处理子属性也可能是序列的情况。但是,在编码未编码的 XML 时,生成的输出只是根元素,没有子元素。

下面是我的输入 XML,表示上述内容序列。

<Root>
  <ps>
    <p name="0040A010" vt="8">HAS CONCEPT MOD</p>
    <p name="0040A040" vt="8">CODE</p>
    <p name="0040A043" vt="8">
      <ps>
        <p name="00080100" vt="8">121049</p>
        <p name="00080102" vt="8">DCM</p>
        <p name="00080104" vt="8">Language of Content Item and Descendants
        </p>
      </ps>
    </p>
    <p name="0040A168" vt="8">
      <ps>
        <p name="00080100" vt="8">eng</p>
        <p name="00080102" vt="8">ISO639_2</p>
        <p name="00080104" vt="8">English</p>
      </ps>
    </p>
  </ps>
  <ps>
    <p name="0040A010" vt="8">HAS OBS CONTEXT</p>
    <p name="0040A040" vt="8">CODE</p>
    <p name="0040A043" vt="8">
      <ps>
        <p name="00080100" vt="8">121005</p>
        <p name="00080102" vt="8">DCM</p>
        <p name="00080104" vt="8">Observer Type</p>
      </ps>
    </p>
    <p name="0040A168" vt="8">
      <ps>
        <p name="00080100" vt="8">121006</p>
        <p name="00080102" vt="8">DCM</p>
        <p name="00080104" vt="8">Person</p>
      </ps>
    </p>
  </ps>
  <ps>
    <p name="0040A010" vt="8">HAS OBS CONTEXT</p>
    <p name="0040A040" vt="8">PNAME</p>
    <p name="0040A043" vt="8">
      <ps>
        <p name="00080100" vt="8">121008</p>
        <p name="00080102" vt="8">DCM</p>
        <p name="00080104" vt="8">Person Observer Name</p>
      </ps>
    </p>
    <p name="0040A123" vt="8">IMAGE</p>
  </ps>
  <ps>
    <p name="00081199" vt="8">
      <ps>
        <p name="00081150" vt="8">1.2.840.10008.5.1.4.1.1.4</p>
        <p name="00081155" vt="8">1.3.6.1.4.1.5962.99.1.3923360762.207819601.1541521685498.13.0
        </p>
        <p name="00081199" vt="8">
          <ps>
            <p name="00081150" vt="8">1.2.840.10008.5.1.4.1.1.11.1</p>
            <p name="00081155" vt="8">1.2.840.114356.2019.12.115.113.18.116.1508.6
            </p>
          </ps>
        </p>
        <p name="00750010" vt="8">GEIIS_IW</p>
        <p name="007510A1" vt="8">1</p>
      </ps>
    </p>
    <p name="0040A010" vt="8">CONTAINS</p>
    <p name="0040A040" vt="8">IMAGE</p>
  </ps>
</Root>

下面是用于映射绑定(bind)上述结构的类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "p")
public class Property
{
   @XmlElementRef(name = "ps", type = PropertySequence.class, required = false)
   @XmlMixed
   protected List<Object> content;

   @XmlAttribute(name = "name", required = true)
   protected String name;

   public List<Object> getContent()
   {
      if (content == null)
      {
         content = new ArrayList<Object>();
      }

      return this.content;
   }

   public String getName()
   {
      return name;
   }

   public void setName(String value)
   {
      this.name = value;
   }
}
@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name = "ps") 
public class PropertySequence 
{ 
   protected List<Property> property; 

   public List<Property> getProperty() 
   { 
      if (property == null) 
      { 
         property = new ArrayList<Property>(); 
      } 

      return this.property; 
   } 
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Root")
public class Root
{
   @XmlElement(required = true)
   protected List<PropertySequence> propertySequence;

   public List<PropertySequence> getPropertySequence()
   {
      if (propertySequence == null)
      {
         propertySequence = new ArrayList<PropertySequence>();
      }

      return this.propertySequence;
   }
}

运行以下测试代码时,解码和后续编码后的输出 XML,输出仅是根标记。

try (InputStream xmlStream = Launcher.class.getResourceAsStream("/PropertySequence.xml")) 
{ 
   JAXBContext context = JAXBContext.newInstance(Root.class); 

   Unmarshaller unmarshaller = context.createUnmarshaller(); 
   Root root = (Root) unmarshaller.unmarshal(xmlStream); 

   Marshaller marshaller = context.createMarshaller(); 
   marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 
   marshaller.marshal(root, System.out);
} 
catch (IOException | JAXBException e) 
{ 
   e.printStackTrace(); 
}

输出

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root/>

最佳答案

XmlMixed 和 XmlElementRef

即使使用 XmlElementRef,我们也无法直接将值反序列化为 PropertySequenceString。不管怎样,使用 JAXBElement 会容易得多。让我们看看模型:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Root", propOrder = {"ps"})
public class Root {

    protected List<PropertySequence> ps;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ps", propOrder = {"p"})
public class PropertySequence {

    protected List<Property> p;

    public List<Property> getP() {
        if (p == null) {
            p = new ArrayList<>();
        }
        return this.p;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (Property property : p) {
            builder.append(property).append(System.lineSeparator());
        }
        return builder.toString();
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "p", propOrder = {"content"})
public class Property {

    @XmlMixed
    @XmlElementRef(name = "ps", type = JAXBElement.class, required = false)
    protected List<Serializable> content;

    @XmlAttribute(name = "name")
    protected String name;

    @XmlAttribute(name = "vt")
    protected String vt;

    @XmlTransient
    public String getStringValue() {
        if (content != null && content.size() == 1) {
            return content.get(0).toString();
        }

        return null;
    }

    @XmlTransient
    public PropertySequence getPropertySequence() {
        if (content != null && content.size() == 3) {
            return ((JAXBElement<PropertySequence>) content.get(1)).getValue();
        }

        return null;
    }

    public List<Serializable> getContent() {
        if (content == null) {
            content = new ArrayList<>();
        }
        return this.content;
    }

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public String getVt() {
        return vt;
    }

    public void setVt(String value) {
        this.vt = value;
    }

    @Override
    public String toString() {
        Object value = getStringValue();
        if (value == null) {
            value = getPropertySequence();
        }
        return "Property{" +
                "content=" + value +
                ", name='" + name + '\'' +
                ", vt='" + vt + '\'' +
                '}';
    }
}

@XmlRegistry
public class ObjectFactory {

    private final static QName ROOT_QNAME = new QName("", "Root");
    private final static QName PropertySequence_QNAME = new QName("", "ps");

    @XmlElementDecl(namespace = "", name = "Root")
    public JAXBElement<Root> createRoot(Root value) {
        return new JAXBElement<>(ROOT_QNAME, Root.class, null, value);
    }

    @XmlElementDecl(namespace = "", name = "ps", scope = Property.class)
    public JAXBElement<PropertySequence> createPropertySequence(PropertySequence value) {
        return new JAXBElement<>(PropertySequence_QNAME, PropertySequence.class, Property.class, value);
    }
}

简单的例子:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;

public class JaxbApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        JAXBContext context = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = context.createUnmarshaller();
        JAXBElement<Root> root = (JAXBElement<Root>) unmarshaller.unmarshal(xmlFile);
        System.out.println(root.getValue());

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(root, System.out);
    }
}

打印模型:

RootType{ps=[Property{content=HAS CONCEPT MOD, name='0040A010', vt='8'}
Property{content=CODE, name='0040A040', vt='8'}
Property{content=Property{content=121049, name='00080100', vt='8'}
Property{content=DCM, name='00080102', vt='8'}
Property{content=Language of Content Item and Descendants, name='00080104', vt='8'}
, name='0040A043', vt='8'}
Property{content=Property{content=eng, name='00080100', vt='8'}
Property{content=ISO639_2, name='00080102', vt='8'}
Property{content=English, name='00080104', vt='8'}
, name='0040A168', vt='8'}
, Property{content=HAS OBS CONTEXT, name='0040A010', vt='8'}
Property{content=CODE, name='0040A040', vt='8'}
Property{content=Property{content=121005, name='00080100', vt='8'}
Property{content=DCM, name='00080102', vt='8'}
Property{content=Observer Type, name='00080104', vt='8'}
, name='0040A043', vt='8'}
Property{content=Property{content=121006, name='00080100', vt='8'}
Property{content=DCM, name='00080102', vt='8'}
Property{content=Person, name='00080104', vt='8'}
, name='0040A168', vt='8'}
, Property{content=HAS OBS CONTEXT, name='0040A010', vt='8'}
Property{content=PNAME, name='0040A040', vt='8'}
Property{content=Property{content=121008, name='00080100', vt='8'}
Property{content=DCM, name='00080102', vt='8'}
Property{content=Person Observer Name, name='00080104', vt='8'}
, name='0040A043', vt='8'}
Property{content=IMAGE, name='0040A123', vt='8'}
, Property{content=Property{content=1.2.840.10008.5.1.4.1.1.4, name='00081150', vt='8'}
Property{content=1.3.6.1.4.1.5962.99.1.3923360762.207819601.1541521685498.13.0
                , name='00081155', vt='8'}
Property{content=Property{content=1.2.840.10008.5.1.4.1.1.11.1, name='00081150', vt='8'}
Property{content=1.2.840.114356.2019.12.115.113.18.116.1508.6
                        , name='00081155', vt='8'}
, name='00081199', vt='8'}
Property{content=GEIIS_IW, name='00750010', vt='8'}
Property{content=1, name='007510A1', vt='8'}
, name='00081199', vt='8'}
Property{content=CONTAINS, name='0040A010', vt='8'}
Property{content=IMAGE, name='0040A040', vt='8'}
]}

XmlMixed 和 XmlAnyElement

它会打印空根,因为反序列化对于您的映射无法正常工作,并且根对象为空(propertySequencenull)。您需要更新 POJO 映射。使用 @XmlAnyElement 代替 @XmlElementRef 注释。进行一些更改后的模型可能如下所示:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Root")
class Root {

    @XmlElement(required = true, name = "ps")
    protected List<PropertySequence> propertySequence;

    // getters, setters, toString
}

@XmlAccessorType(XmlAccessType.FIELD)
class PropertySequence {

    @XmlElement(name = "p")
    protected List<Property> property;

    // getters, setters, toString
}


@XmlAccessorType(XmlAccessType.FIELD)
class Property {

    @XmlAttribute(name = "name", required = true)
    protected String name;

    @XmlAttribute(name = "vt", required = true)
    protected Integer vt;

    @XmlMixed
    @XmlAnyElement
    protected List<Object> value;

    // getters, setters, toString
}

下面的代码:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class JaxbApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        JAXBContext context = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = context.createUnmarshaller();
        Root root = (Root) unmarshaller.unmarshal(xmlFile);
        System.out.println(root);

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(root, System.out);
    }
}

打印对象:

Root{propertySequence=[PropertySequence{property=[Property{name='0040A010', vt=8, value=[HAS CONCEPT MOD]}, Property{name='0040A040', vt=8, value=[CODE]}, Property{name='0040A043', vt=8, value=[
            , [ps: null], 
        ]}, Property{name='0040A168', vt=8, value=[
            , [ps: null], 
        ]}]}, PropertySequence{property=[Property{name='0040A010', vt=8, value=[HAS OBS CONTEXT]}, Property{name='0040A040', vt=8, value=[CODE]}, Property{name='0040A043', vt=8, value=[
            , [ps: null], 
        ]}, Property{name='0040A168', vt=8, value=[
            , [ps: null], 
        ]}]}, PropertySequence{property=[Property{name='0040A010', vt=8, value=[HAS OBS CONTEXT]}, Property{name='0040A040', vt=8, value=[PNAME]}, Property{name='0040A043', vt=8, value=[
            , [ps: null], 
        ]}, Property{name='0040A123', vt=8, value=[IMAGE]}]}, PropertySequence{property=[Property{name='00081199', vt=8, value=[
            , [ps: null], 
        ]}, Property{name='0040A010', vt=8, value=[CONTAINS]}, Property{name='0040A040', vt=8, value=[IMAGE]}]}]}

XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
    <ps>
        <p name="0040A010" vt="8">HAS CONCEPT MOD</p>
        <p name="0040A040" vt="8">CODE</p>
        <p name="0040A043" vt="8">
            <ps>
                <p name="00080100" vt="8">121049</p>
                <p name="00080102" vt="8">DCM</p>
                <p name="00080104" vt="8">Language of Content Item and Descendants</p>
            </ps>
        </p>
        <p name="0040A168" vt="8">
            <ps>
                <p name="00080100" vt="8">eng</p>
                <p name="00080102" vt="8">ISO639_2</p>
                <p name="00080104" vt="8">English</p>
            </ps>
        </p>
    </ps>
    <ps>
        <p name="0040A010" vt="8">HAS OBS CONTEXT</p>
        <p name="0040A040" vt="8">CODE</p>
        <p name="0040A043" vt="8">
            <ps>
                <p name="00080100" vt="8">121005</p>
                <p name="00080102" vt="8">DCM</p>
                <p name="00080104" vt="8">Observer Type</p>
            </ps>
        </p>
        <p name="0040A168" vt="8">
            <ps>
                <p name="00080100" vt="8">121006</p>
                <p name="00080102" vt="8">DCM</p>
                <p name="00080104" vt="8">Person</p>
            </ps>
        </p>
    </ps>
    <ps>
        <p name="0040A010" vt="8">HAS OBS CONTEXT</p>
        <p name="0040A040" vt="8">PNAME</p>
        <p name="0040A043" vt="8">
            <ps>
                <p name="00080100" vt="8">121008</p>
                <p name="00080102" vt="8">DCM</p>
                <p name="00080104" vt="8">Person Observer Name</p>
            </ps>
        </p>
        <p name="0040A123" vt="8">IMAGE</p>
    </ps>
    <ps>
        <p name="00081199" vt="8">
            <ps>
                <p name="00081150" vt="8">1.2.840.10008.5.1.4.1.1.4</p>
                <p name="00081155" vt="8">1.3.6.1.4.1.5962.99.1.3923360762.207819601.1541521685498.13.0
                </p>
                <p name="00081199" vt="8">
                    <ps>
                        <p name="00081150" vt="8">1.2.840.10008.5.1.4.1.1.11.1</p>
                        <p name="00081155" vt="8">1.2.840.114356.2019.12.115.113.18.116.1508.6
                        </p>
                    </ps>
                </p>
                <p name="00750010" vt="8">GEIIS_IW</p>
                <p name="007510A1" vt="8">1</p>
            </ps>
        </p>
        <p name="0040A010" vt="8">CONTAINS</p>
        <p name="0040A040" vt="8">IMAGE</p>
    </ps>
</Root>

关于java - 使用混合类型时编码 XML 没有子级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55313978/

相关文章:

java - Envers、PostgreSQL 和 TINYINT

java - 编码时单组元素的 JAXB 元素包装器

java - UnmarshalException : unexpected element (uri :"http://www.namespace.com/RTS", 本地 :"container")

c# - 如何在 Web API 响应中排除 Xml 序列化中的属性名称

java - 为什么我的 fragment 是空白的?

java - 解码时对象方法出现问题?

java - JAXB Umarshaller 错误

java - 将 Objective-c 的枚举转换为 Android 的枚举

java - Thymeleaf 返回 String 数组而不是 List<Object>

java - 如何在java JAX-WS应用程序中获取WEB-INF文件夹路径