简体   繁体   中英

JAXB Unmarshalling to Java POJO

I want to convert this XMl into java Object but nit getting how to make POJO class for it. I can not change this xml because it is comning from remote server as a request and I need all information that it contains in order to response.

<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>name</methodName>
<params>
<param><value><struct>
<member>
<name>subscriberInput</name>
<value><string>678</string></value>
</member>
<member>
<name>language</name>
<value><string>en</string></value>
</member>
<member>
<name>sessionId</name>
<value><string>16414746570268014</string></value>
</member>
<member><name>msisdn</name><value><string>1234</string></value>
</member>
<member>
<name>newRequest</name>
<value><string>1</string></value>
</member>
<member>
<name>transactionId</name>
<value><string>0122716414746578950</string>
</value>
</member>
</struct>
</value>
</param>
</params>\</methodCall>

You could use the combination of the following tools:

  1. Parse XML to JSON ( https://www.convertjson.com/xml-to-json.htm )
  2. Create POJOs from JSON ( https://json2csharp.com/json-to-pojo )

The result is the next:

// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1
// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1
/* ObjectMapper om = new ObjectMapper();
Root root = om.readValue(myJsonString), Root.class); */
public class Value2{
    public String string;
    public Struct struct;
}

public class Member{
    public String name;
    public Value value;
}

public class Struct{
    public List<Member> member;
}

public class Param{
    public Value value;
}

public class Params{
    public Param param;
}

public class MethodCall{
    public String methodName;
    public Params params;
}

public class Root{
    public MethodCall methodCall;
}

I hope I was able to help you.

Pojo Approach

Taking this xml to https://codebeautify.org/xml-to-java-converter , it produces us ("quickly", "good") results:

We can copy&paste the generated class, but still need to refactor little:

  • un-public all classes (or move them to separate files)
  • complete getters/setters of Struct

But, we "suspect already": The problem is in the details and details are in the structure:

package com.example.simpleschema;

import java.util.ArrayList;

public class MethodCall {

  private String methodName;
  Params ParamsObject;

  // Getter Methods 
  public String getMethodName() {
    return methodName;
  }

  public Params getParams() {
    return ParamsObject;
  }

  // Setter Methods 
  public void setMethodName(String methodName) {
    this.methodName = methodName;
  }

  public void setParams(Params paramsObject) {
    this.ParamsObject = paramsObject;
  }
}

class Params {

  Param ParamObject;

  // Getter Methods 
  public Param getParam() {
    return ParamObject;
  }

  // Setter Methods 
  public void setParam(Param paramObject) {
    this.ParamObject = paramObject;
  }
}

class Param {

  Value ValueObject;

  // Getter Methods 
  public Value getValue() {
    return ValueObject;
  }

  // Setter Methods 
  public void setValue(Value valueObject) {
    this.ValueObject = valueObject;
  }
}

class Value {

  Struct StructObject;

  // Getter Methods 
  public Struct getStruct() {
    return StructObject;
  }

  // Setter Methods 
  public void setStruct(Struct structObject) {
    this.StructObject = structObject;
  }
}

class Struct {

  ArrayList< Object> member = new ArrayList< Object>();

  public ArrayList<Object> getMember() {
    return member;
  }

  public void setMember(ArrayList<Object> member) {
    this.member = member;
  }

}

With (only) this in our pom:

  <dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.13.1</version> <!-- or newer --> 
  </dependency>

We can do that:

package com.example.xmltest.simple;

import com.example.simpleschema.MethodCall;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;

public class MainSimple {

  public static void main(String[] args) throws IOException {
    final XmlMapper xmlMapper = new XmlMapper();
    MethodCall value = xmlMapper
        .readValue(MainSimple.class.getResourceAsStream("/test.xml"), MethodCall.class);
    System.out.println(value);
    xmlMapper.writeValue(System.out, value);
  }
}

Prints:

com.example.simpleschema.MethodCall@23529fee
<MethodCall><methodName>name</methodName><params><param><value><struct><member><member>transactionId</member><member><string>0122716414746578950</string></member></member></struct></value></param></params></MethodCall>

With JAXB

We need to an xsd first: We can:

  • ask our provider, if he would supply us... or
  • we generate it ourself...

A "quick" lookup leads us (nowadays, with open-source) to https://docs.microsoft.com/en-us/visualstudio/xml-tools/how-to-create-an-xml-schema-from-an-xml-document . click-clack in , and our xsd looks like:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="methodCall">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="methodName" type="xs:string" />
        <xs:element name="params">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="param">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="value">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="struct">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="member" maxOccurs="unbounded">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="name" type="xs:string" />
                                      <xs:element name="value">
                                        <xs:complexType>
                                          <xs:sequence>
                                            <xs:element name="string" type="xs:string" />
                                          </xs:sequence>
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Putting this test.xsd under src/main/xsd (standard loaction of codehaus:jaxb2-maven-plugin), and add the following to our pom (sorry @all gradel-users):

...
 <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>2.5.0</version> <!-- this depends on org.glassfish.jaxb:2.3.2!! -->
            <executions>
              <execution>
                <id>xjc</id>
                <goals>
                  <goal>xjc</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <!-- The (target) package of your generated sources -->
              <packageName>com.example.jaxbschema</packageName> <!-- .${project.version} -->
            </configuration>
          </plugin>
        </plugins>
      </build>
...

On the next mvn process-classes (or above/or accomplished by IDE), this will generate us some "fine" classes in src/target/generated-sources/jaxb/ ... These are not compilable,, (Or the structure is too recursive, or it is a jaxb(impl)-bug, consider it as you like)


We can fix!

Manually... but have to move the generated com.example.myschema to "main" sources, de-activate the jaxb-plugin, and fix the name clash caused by the most inner Value :

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "value", propOrder = {
  "string"
})
public static class Value2 { ...

... and resolving remaining compiler conflicts.


This (generated&adjusted model) seems still to be buggy at least for jackson.


With this, and :

...
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.2</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!-- Spring OXM: needs jaxb -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <!-- managed by spring-boot: 5.3.14 -->
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <!-- managed by spring-boo: 2.3.1 -->
    </dependency>
    <dependency>
      <groupId>org.glassfish.jaxb</groupId>
      <artifactId>jaxb-runtime</artifactId>
      <scope>runtime</scope>
      <!-- managed by spring-boo: 2.3.5 -->
    </dependency>
  </dependencies>
...

We can:

// ...
import com.example.jaxbschema.MethodCall; // !! same as generated/refactored.
// ...
@SpringBootApplication
public class SpringMain {

  public static void main(String[] args) {
    SpringApplication.run(SpringMain.class, args);
  }

  @Bean
  org.springframework.oxm.jaxb.Jaxb2Marshaller jaxb2Marshaller() {
    Jaxb2Marshaller mler = new org.springframework.oxm.jaxb.Jaxb2Marshaller();
    mler.setClassesToBeBound(MethodCall.class);
    return mler;
  }

  @Bean
  @Autowired
  CommandLineRunner runner(
      @Value("test.xml") Resource xmlFile,
      org.springframework.oxm.jaxb.Jaxb2Marshaller jaxb2Marshaller
  ) {
    return (args) -> {
      MethodCall readValue2 = (MethodCall) jaxb2Marshaller.unmarshal(new StreamSource(xmlFile.getInputStream()));
      jaxb2Marshaller.marshal(readValue2, new StreamResult(System.out));
    };
  }

}

...which prints:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><methodCall><methodName>name</methodName><params><param><value><struct><member><name>subscriberInput</name><value><string>678</string></value></member><member><name>language</name><value><string>en</string></value></member><member><name>sessionId</name><value><string>16414746570268014</string></value></member><member><name>msisdn</name><value><string>1234</string></value></member><member><name>newRequest</name><value><string>1</string></value></member><member><name>transactionId</name><value><string>0122716414746578950</string></value></member></struct></value></param></params></methodCall>

When Structure/XML is big/complex

..and we only need "few values" form that, we should consider a more effcient (parser based) approach...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM