sairn
sairn

Reputation: 490

why is this modelmapper mapping failing?

I'm trying to map a json pojo to an xml pojo using ModelMapper...

In the code, below, I was expecting to see the content of "jsonSource" (source) POJO transferred to xmlDest POJO (target)

...however, the code fails - as no data in ("jsonSource") was reflected in the target ("xml"). i.e., as the difference in the ".size()" method value indicates.

SOURCE (json)

jsonSource.getEmployees().size() == 2

TARGET (xml)

xml.getEmployees().size() == 0    <<< should also be 2

code snippet...

    @Test
    public void givenJsonSource_whenMapToXmlDest_thenFieldsMapCorrectly() throws Exception {

        Employees jsonSource = new Employees();
        List<Employee> employees = new ArrayList<>();

        Employee employee1 = new Employee();
        employee1.setName("thename");
        employee1.setGender("M");
        employee1.setAge(30);
        employees.add(employee1);

        Employee employee2 = new Employee();
        employee2.setName("thename2");
        employee2.setGender("M2");
        employee2.setAge(32);
        employees.add(employee2);

        jsonSource.setEmployees(employees);

        aaa.bbb.ccc.jar.generated.Employees xml = mapper.map(jsonSource, aaa.bbb.ccc.jar.generated.Employees.class);

        System.out.println("jsonSource.getEmployees().size()=>" + jsonSource.getEmployees().size());  // <<< 2
        System.out.println("xmlDest.getEmployee().size()=>" + xml.getEmployees().size());  // <<< 0


        // verify fields
        assertEquals(xml.getEmployees().get(0).getName(), jsonSource.getEmployees().get(0).getName());
    }
    

...although jsonSource.getEmployees().size is 2, the xmlDest.getEmployee.size() is 0

Here is the Employee json pojo class (referenced above)...

    package aaa.bbb.ccc.jar;

    import com.fasterxml.jackson.annotation.JsonProperty;

    public class Employee {

        @JsonProperty("name") 
        String name;
        @JsonProperty("gender")     
        String gender;
        @JsonProperty("age")     
        Integer age;
        
        public Employee() {
        }
        public Employee(String name, String gender, Integer age) {
            this.name = name;
            this.gender = gender;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getGender() {
            return gender;
        }
        public void setGender(String gender) {
            this.gender = gender;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "Employee{" + "name=" + name + ", gender=" + gender + ", age=" + age + '}';
        }

    }

Here is the Employees json POJO class...

    package aaa.bbb.ccc.jar;

    import java.util.ArrayList;
    import java.util.List;

    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonRootName;

    @JsonRootName(value = "Employees")
    public class Employees {
        
        @JsonProperty(value = "Employees")
        protected List<Employee> employees;

        public Employees() {
        }

        public List<Employee> getEmployees() {
            if (employees == null) {
                employees = new ArrayList<>();
            }
            return this.employees;
        }

        public void setEmployees(List<Employee> employees) {
            this.employees = employees;
        }

        @Override
        public String toString() {
            return "Employees{" + "employees=" + employees + '}';
        }
        
    }

Here is the generated xml POJO (aaa.bbb.ccc.jar.generated.Employee)

    //
    // This file was generated by the Eclipse Implementation of JAXB, v3.0.0 
    // See https://eclipse-ee4j.github.io/jaxb-ri 
    // Any modifications to this file will be lost upon recompilation of the source schema. 
    // Generated on: 2024.04.12 at 08:27:40 AM EDT 
    //


    package aaa.bbb.ccc.jar.generated;

    import jakarta.xml.bind.annotation.XmlAccessType;
    import jakarta.xml.bind.annotation.XmlAccessorType;
    import jakarta.xml.bind.annotation.XmlElement;
    import jakarta.xml.bind.annotation.XmlType;


    /**
     * <p>Java class for Employee complex type.
     * 
     * <p>The following schema fragment specifies the expected content contained within this class.
     * 
     * <pre>
     * &lt;complexType name="Employee"&gt;
     *   &lt;complexContent&gt;
     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
     *       &lt;all&gt;
     *         &lt;element name="Name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
     *         &lt;element name="Gender" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
     *         &lt;element name="Age" type="{http://www.w3.org/2001/XMLSchema}int" minOccurs="0"/&gt;
     *       &lt;/all&gt;
     *     &lt;/restriction&gt;
     *   &lt;/complexContent&gt;
     * &lt;/complexType&gt;
     * </pre>
     * 
     * 
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "Employee", propOrder = {

    })
    public class Employee {

        @XmlElement(name = "Name")
        protected String name;
        @XmlElement(name = "Gender")
        protected String gender;
        @XmlElement(name = "Age")
        protected Integer age;

        /**
         * Gets the value of the name property.
         * 
         * @return
         *     possible object is
         *     {@link String }
         *     
         */
        public String getName() {
            return name;
        }

        /**
         * Sets the value of the name property.
         * 
         * @param value
         *     allowed object is
         *     {@link String }
         *     
         */
        public void setName(String value) {
            this.name = value;
        }

        /**
         * Gets the value of the gender property.
         * 
         * @return
         *     possible object is
         *     {@link String }
         *     
         */
        public String getGender() {
            return gender;
        }

        /**
         * Sets the value of the gender property.
         * 
         * @param value
         *     allowed object is
         *     {@link String }
         *     
         */
        public void setGender(String value) {
            this.gender = value;
        }

        /**
         * Gets the value of the age property.
         * 
         * @return
         *     possible object is
         *     {@link Integer }
         *     
         */
        public Integer getAge() {
            return age;
        }

        /**
         * Sets the value of the age property.
         * 
         * @param value
         *     allowed object is
         *     {@link Integer }
         *     
         */
        public void setAge(Integer value) {
            this.age = value;
        }

    }

Here is the generated xml pojo (i.e., aaa.bbb.ccc.jar.generated.Employees)

    //
    // This file was generated by the Eclipse Implementation of JAXB, v3.0.0 
    // See https://eclipse-ee4j.github.io/jaxb-ri 
    // Any modifications to this file will be lost upon recompilation of the source schema. 
    // Generated on: 2024.04.12 at 08:27:40 AM EDT 
    //


    package aaa.bbb.ccc.jar.generated;

    import java.util.ArrayList;
    import java.util.List;
    import jakarta.xml.bind.annotation.XmlAccessType;
    import jakarta.xml.bind.annotation.XmlAccessorType;
    import jakarta.xml.bind.annotation.XmlElement;
    import jakarta.xml.bind.annotation.XmlRootElement;
    import jakarta.xml.bind.annotation.XmlType;


    /**
     * <p>Java class for anonymous complex type.
     * 
     * <p>The following schema fragment specifies the expected content contained within this class.
     * 
     * <pre>
     * &lt;complexType&gt;
     *   &lt;complexContent&gt;
     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
     *       &lt;sequence&gt;
     *         &lt;element name="Employees" type="{http://www.aaa.bbb.ccc/jar/mmdemo}Employee" maxOccurs="unbounded" minOccurs="0"/&gt;
     *       &lt;/sequence&gt;
     *     &lt;/restriction&gt;
     *   &lt;/complexContent&gt;
     * &lt;/complexType&gt;
     * </pre>
     * 
     * 
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "employees"
    })
    @XmlRootElement(name = "Employees")
    public class Employees {

        @XmlElement(name = "Employees")
        protected List<Employee> employees;

        /**
         * Gets the value of the employees property.
         * 
         * <p>
         * This accessor method returns a reference to the live list,
         * not a snapshot. Therefore any modification you make to the
         * returned list will be present inside the Jakarta XML Binding object.
         * This is why there is not a <CODE>set</CODE> method for the employees property.
         * 
         * <p>
         * For example, to add a new item, do as follows:
         * <pre>
         *    getEmployees().add(newItem);
         * </pre>
         * 
         * 
         * <p>
         * Objects of the following type(s) are allowed in the list
         * {@link Employee }
         * 
         * 
         */
        public List<Employee> getEmployees() {
            if (employees == null) {
                employees = new ArrayList<Employee>();
            }
            return this.employees;
        }

    }

this is the xsd used to generate the XML classes

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema 
        targetNamespace="http://www.aaa.bbb.ccc/jar/mmdemo"
        attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        xmlns:es="http://www.aaa.bbb.ccc/jar/mmdemo"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
                
        <xs:complexType name="Employee">
            <xs:all>
                
                <xs:element name="Name" type="xs:string" minOccurs="0" />
                <xs:element name="Gender" type="xs:string" minOccurs="0" />
                <xs:element name="Age" type="xs:int" minOccurs="0" />
                                                        
            </xs:all>
        </xs:complexType>

        <xs:element name="Employees">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="Employees" type="es:Employee" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>

    </xs:schema>

appreciate any help/hints at understanding what might be missing that would make the above work

Upvotes: 0

Views: 46

Answers (1)

sairn
sairn

Reputation: 490

looks like "a" solution is to individually map elements of Employees in a for-loop...

        for (Employee sourceEmployee : sourceEmployees) {
            aaa.bbb.ccc.jar.generated.Employee destEmployee = mapper.map(sourceEmployee, aaa.bbb.ccc.jar.generated.Employee.class);
            xml.getEmployees().add(destEmployee);
        }   

almost certainly it's not necessarily the only solution -but, it works now....

    @Test
    public void givenJsonSource_whenMapToXmlDest_thenFieldsMapCorrectly() throws Exception {

        Employees jsonSource = new Employees();
        List<Employee> employees = new ArrayList<>();

        Employee employee1 = new Employee();
        employee1.setName("thename");
        employee1.setGender("M");
        employee1.setAge(30);
        employees.add(employee1);

        Employee employee2 = new Employee();
        employee2.setName("thename2");
        employee2.setGender("M2");
        employee2.setAge(32);
        employees.add(employee2);

        jsonSource.setEmployees(employees);
        
        aaa.bbb.ccc.jar.generated.Employees xml = new aaa.bbb.ccc.jar.generated.Employees();
        
        List<Employee> sourceEmployees = jsonSource.getEmployees();
        for (Employee sourceEmployee : sourceEmployees) {
            aaa.bbb.ccc.jar.generated.Employee destEmployee = mapper.map(sourceEmployee, aaa.bbb.ccc.jar.generated.Employee.class);
            xml.getEmployees().add(destEmployee);
        }        

        System.out.println("jsonSource.getEmployees().size()=>" + jsonSource.getEmployees().size());
        System.out.println("xmlDest.getEmployee().size()=>" + xml.getEmployees().size());


        // verify fields
        assertEquals(xml.getEmployees().get(0).getName(), jsonSource.getEmployees().get(0).getName());
    }

Upvotes: 0

Related Questions