0

I'm using spring batch to generate xml files.

My writer looks like :

Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Person.class);

StaxEventItemWriter<Person> itemWriter = new StaxEventItemWriter<>();
itemWriter.setRootTagName("Persons");
itemWriter.setMarshaller(marshaller);
itemWriter.setRootElementAttributes(new HashMap<String, String>() {{
        put("xmlns", "http://entreprise.uk/ns");
}});
itemWriter.setResource(new FileSystemResource(Paths.get("personOutput.xml").toFile()));
itemWriter.afterPropertiesSet();

return itemWriter;

And the person class :

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Person")
public class Person {
  //...
}

When I run the batch I get this error :

Caused by: javax.xml.stream.XMLStreamException: xmlns has been already bound to . Rebinding it to http://entreprise.uk/ns is an error

Anyone knows how to fix it ? I need to see xmlns attribut at the root element like :

<?xml version="1.0" encoding="UTF-8"?>
<Persons xmlns="http://entreprise.uk/ns">
    <person>...</person>
</Persons>

I'm using spring-boot-starter-batch:2.3.5.RELEASE

flywell
  • 384
  • 3
  • 20
  • You might want to check out https://stackoverflow.com/questions/67975111/xmlstreamexception-xmlns-has-been-already-bound-to-rebinding-it-to-http-deu/68871980#68871980. Had a similar problem, got it fixed and posted the answer there. – truekiller Aug 21 '21 at 10:05

3 Answers3

2

To add namespace at the root level, you have to modify the rootTagName in your configuration.

rootTagName("{http://entreprise.uk/ns}Persons")

Hope this solves your problem.

truekiller
  • 470
  • 6
  • 19
0

I would personally go with a container class called Persons that has an array/list of Person.

@XmlRootElement(name = "Persons", namespace = "http://entreprise.uk/ns")
@XmlAccessorType(XmlAccessType.FIELD)
public class Persons {

  @XmlElement(name = "Person")
  private List<Person> persons;

  //...
}

This should also remove the need for some of the configurations you currently have to set.

BuzZin'
  • 441
  • 1
  • 5
  • 10
  • sorry its not woking, `setRootTagName` should be used with `Persons`. Otherwise `root` will be used. – flywell Aug 20 '21 at 13:20
0

There are 2 ways to solve this issue:

Method-1 Using the package-info file:

XML:

<?xml version="1.0" encoding="UTF-8"?>
<Persons xmlns="http://entreprise.uk/ns">
    <person>
        <firstName>Batman</firstName>
    </person>
</Persons>

package-info.java:

@XmlSchema(
        elementFormDefault = XmlNsForm.QUALIFIED,
        namespace = "http://entreprise.uk/ns",
        xmlns = {@XmlNs(prefix = "", namespaceURI = "http://entreprise.uk/ns")})
package stackover;

import jakarta.xml.bind.annotation.XmlNs;
import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;

Persons:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Persons")
@Data
public class Persons {
    private Person person;
}

Person:

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
    private String firstName;
}

Main:


public class Main {
    public static void main(String[] args) throws JAXBException, XMLStreamException {
        final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xml");
        final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
        final Unmarshaller unmarshaller = JAXBContext.newInstance(Persons.class).createUnmarshaller();
        final Persons persons = unmarshaller.unmarshal(xmlStreamReader, Persons.class).getValue();
        System.out.println(persons.toString());

        Marshaller marshaller = JAXBContext.newInstance(Persons.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(persons, System.out);
    }
}

Output:

Persons(person=Person(firstName=Batman))
<Persons xmlns="http://entreprise.uk/ns">
   <person>
      <firstName>Batman</firstName>
   </person>
</Persons>

Method-2 Using the prefix with namespace URI for XML and adding to the root class:

XML:

<?xml version="1.0" encoding="UTF-8"?>
<Persons xmlns:ns0="http://entreprise.uk/ns">
    <person>
        <firstName>Batman</firstName>
    </person>
</Persons>

Persons:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Persons", namespace = "http://entreprise.uk/ns")
@Data
public class Persons {
    private Person person;
}

Person:

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
    private String firstName;
}

Main:

public class Main {
    public static void main(String[] args) throws JAXBException, XMLStreamException {
        final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xml");
        final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
        final Unmarshaller unmarshaller = JAXBContext.newInstance(Persons.class).createUnmarshaller();
        final Persons persons = unmarshaller.unmarshal(xmlStreamReader, Persons.class).getValue();
        System.out.println(persons.toString());

        Marshaller marshaller = JAXBContext.newInstance(Persons.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(persons, System.out);
    }
}

Output:

Persons(person=Person(firstName=Batman))
<ns0:Persons xmlns:ns0="http://entreprise.uk/ns">
   <person>
      <firstName>Batman</firstName>
   </person>
</ns0:Persons>
BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98