Peter Smith
Peter Smith

Reputation: 889

Extending third party existing JAXB class

I have a third party library that is providing a set of classes. These classes comprise an object graph for an XML structure and use JAXB for XML (un)marshaling. Let's say I have classes Car, Axle and Tire.

Car contains a list of Axle and Axle contains a list of Tire.

Tire looks like

public class Tire {
    private double width;
    private double radius;

    public double getWidth() {
       return width;
    }
    public double getRadius() {
       return radius;
    }
}

Now I want to add a property called pressure to Tire

public class PressurizedTire extends Tire {
    private double pressure;

    public PressurizedTire(Tire t, double pressure) {
      this.width = t.getWidth();
      this.radius = t.getRadius();
      this.pressure = pressure;
    }
 }

I'd like to deserialize an xml document containing the car using JAXB, find all the tires and add the pressure data to each tire. I'd then like to reserialize the message to XML. Currently the extra property pressure gets dropped upon marshalling and the object structure goes back to it's original form.

What's the best way to accomplish adding properties to an existing JAXB model class? I don't want to extend every class, and I'm unsure about rebuilding and modifying the .xsd to accomplish this?

Upvotes: 1

Views: 1256

Answers (2)

Max Fichtelmann
Max Fichtelmann

Reputation: 3504

I think this should work

I have just tried with the following classes:

@XmlAccessorType( XmlAccessType.FIELD )
public class Tire
{
    double width;
    double radius;
}

@XmlRootElement
@XmlAccessorType( XmlAccessType.FIELD )
public class Axle
{
    @XmlElement( name = "tire" )
    List<Tire> tires = new ArrayList<>();
}

@XmlAccessorType( XmlAccessType.FIELD )
public class PressurizedTire extends Tire
{
    double pressure;

    public PressurizedTire( Tire t, double pressure )
    {
        this.width = t.width;
        this.radius = t.radius;
        this.pressure = pressure;
    }
}

and the following code

    JAXBContext context = JAXBContext.newInstance( Axle.class, PressurizedTire.class );
    Unmarshaller unmarshaller = context.createUnmarshaller();

    String xml = "<axle><tire><width>2.0</width><radius>1.5</radius></tire><tire><width>2.5</width><radius>1.5</radius></tire></axle>";
    Axle axle = (Axle) unmarshaller.unmarshal( new StringReader( xml ) );

    List<Tire> pressurizedTires = new ArrayList<>();
    for ( Tire tire : axle.tires )
    {
        pressurizedTires.add( new PressurizedTire( tire, 1.0 ) );
    }
    axle.tires = pressurizedTires;

    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
    marshaller.marshal( axle, System.out );

and got this output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<axle>
    <tire xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pressurizedTire">
        <width>2.0</width>
        <radius>1.5</radius>
        <pressure>1.0</pressure>
    </tire>
    <tire xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pressurizedTire">
        <width>2.5</width>
        <radius>1.5</radius>
        <pressure>1.0</pressure>
    </tire>
</axle>

Upvotes: 1

K139
K139

Reputation: 3669

Yes, you can do that, and it is called Adding behavior to existing JAXB classes.

Below example taken from above link: (All you need to do is @Override the object creation also. If you miss it, it won't work).

package org.acme.foo.impl;

class PersonEx extends Person {
  @Override
  public void setName(String name) {
    if(name.length()<3) throw new IllegalArgumentException();
    super.setName(name);
  }
}

@XmlRegistry
class ObjectFactoryEx extends ObjectFactory {
  @Override
  Person createPerson() {
    return new PersonEx();
  }
}

Upvotes: 1

Related Questions