java 解包 JAXB_FRAGMENT

问题描述 投票:0回答:1

我有一个从 xsd 生成的对象:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "myClass", propOrder = {
    "id",
    "code"
})
public class MyClass {

    @XmlElement(required = true)
    protected String id;
    @XmlElement(required = true)
    protected String code;
    ...

}

我得到一个字符串:

<id>1234dddd</id><code>ddsw</code>

我想将此字符串解压缩为

MyClass
的对象。

try (StringReader reader = new StringReader(xml)) {
    JAXBContext jaxbContext = JAXBContext.newInstance(MyClass.class);
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    JAXBElement<MyClass> jaxbElement = (JAXBElement<MyClass>) unmarshaller.unmarshal(reader);
    return  jaxbElement.getValue();
} catch (Exception e) {
    throw new RuntimeException("Ошибка десериализации", e);
}

但是我得到了一个错误:

unexpected element (uri:"", local:"id"). Expected elements are (none)
我无法更改 MyClass 因为它是生成的,我如何从字符串中获取它的对象?

java xml jaxb
1个回答
0
投票

此处显示的解决方案执行以下两件事:

  1. 它使用特定的
    Unmarshaller
    方法来声明
    MyClass
    为目标对象。
  2. 它使用
    MyClassStringReaer
    将 XML 片段封装在
    <myClass>...</mClass>
    根元素中。

输出为:

myClass: org.example.myclass.MyClass@6b9651f3
  id...: 1234dddd
  code.: ddsw

当 JAXB 类未声明自身为

unmarshal
时,可以使用此
@XmlRootElement
方法。它显式地声明了目标类。

<T> JAXBElement<T> unmarshal(javax.xml.transform.Source source, Class<T> declaredType)

虽然该方法需要 XML

Source
参数,但这可以轻松地从 I/O 读取器或流对象创建,如演示中所示。

此演示使用名为

MyClassStringReader
的自定义阅读器类,请参阅下文。可以使用
<myClass>...</myClass>
连接将要解组的 XML 片段包装在
String
根元素中,而不是使用此方法;但是,自定义读取器是 I/O 装饰的一个很好的演示,它可以用作文件或其他基于流的读取器的模板,在这些读取器中串联是一个糟糕的选择。

演示.java

package org.example.myclass;

import java.io.StringReader;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.Unmarshaller;

public class Demo
{
    @SuppressWarnings("unchecked")
    public static void main(String[] args)
    {
        String txt = "<id>1234dddd</id><code>ddsw</code>";
        try
        {
            JAXBContext jaxbContext = JAXBContext.newInstance(MyClass.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            try (StringReader reader = new MyClassStringReader(txt))
            {
                // reader: "<myClass><id>1234dddd</id><code>ddsw</code></myClass>";
                Source source = new StreamSource(reader);
                Object obj = unmarshaller.unmarshal(source, MyClass.class);
                if ( obj instanceof JAXBElement )
                {
                    MyClass myClass = ((JAXBElement<MyClass>) obj).getValue();
                    println("myClass: " + myClass);
                    println("  id...: " + myClass.id);
                    println("  code.: " + myClass.code);
                }
                else
                    println("Object.:" + obj);
            }
        }
        catch (Exception ex)
        {
            throw new RuntimeException("Ошибка десериализации", ex);
        }
    }

    private static void println(String str)
    {
        System.out.println(str);
    }
}

这个

MyClassStringReader
类扩展了
java.io.StringReader
,以用所需的根元素封装仅包含 myClass 元素的子元素的任何字符串。 XML 始终需要一个根元素。它通过将大部分工作委托给其
Reader
超级实例来覆盖抽象
StringReader
使用的所有公共方法。这是一个快速实现,需要进行彻底的测试才能更广泛地使用。

MyClassStringReader.java

package org.example.myclass; import java.io.IOException; import java.io.StringReader; /** * Envelop a {@link StringReader} with a root element. */ public class MyClassStringReader extends StringReader { private static final String ROOT_OPEN = "<myClass>"; private static final String ROOT_CLOSE = "</myClass>"; private final StringReader ROOT_OPEN_READER = new StringReader(ROOT_OPEN); private final StringReader ROOT_CLOSE_READER = new StringReader(ROOT_CLOSE); /** * Construct with string. * @param str The string to stream. */ public MyClassStringReader(String str) { super(str); } @Override public int read() throws IOException { int nextChar = ROOT_OPEN_READER.read(); if ( nextChar < 0 ) { nextChar = super.read(); if ( nextChar < 0 ) nextChar = ROOT_CLOSE_READER.read(); } return nextChar; } @Override public int read(char[] cbuf, int off, int len) throws IOException { // Guard len if ( len > cbuf.length) len = cbuf.length; // Skip offset characters int nextChar = 0; while ( (--off > 0) && ((nextChar = read()) >= 0) ); // Read len or less characters int counter = 0; if ( nextChar >= 0 ) { while ( (counter < len) && ((nextChar = read()) >= 0) ) cbuf[counter++] = (char) nextChar; } // Return number of characters read or EOS return (counter > 0 ) ? counter : -1; } @Override public boolean ready() throws IOException { return ROOT_OPEN_READER.ready() || super.ready() || ROOT_CLOSE_READER.ready(); } @Override public void reset() throws IOException { ROOT_OPEN_READER.reset(); super.reset(); ROOT_CLOSE_READER.reset(); } @Override public long skip(long n) throws IOException { long s = n; if ( n > 0L ) { if ( ROOT_OPEN_READER.ready() ) s -= ROOT_OPEN_READER.skip(s); if ( (s > 0) && super.ready() ) s -= super.skip(s); if ( (s > 0) && ROOT_CLOSE_READER.ready() ) s -= ROOT_CLOSE_READER.skip(s); } else if ( n < 0L ) throw new IllegalArgumentException("skip value is negative"); return (n-s); } @Override public void mark(int readAheadLimit) throws IOException { if ( ROOT_OPEN_READER.ready() ) ROOT_OPEN_READER.mark(readAheadLimit); else if (super.ready() ) super.mark(readAheadLimit); else if ( ROOT_CLOSE_READER.ready() ) ROOT_CLOSE_READER.mark(readAheadLimit); } }

© www.soinside.com 2019 - 2024. All rights reserved.