开发者

How to ignore casing of element names in JaxB

As stated in the title I want to ignore casing of element names in a document.

static class XY433 {
    @XmlAttribute(name = "C200")
    String c200;
    @XmlAttribute(name = "C215")
    String c215;

    @XmlAttribute(name="F001")
    String f001;

    @XmlAttribute(name="f001")
    String lcf001; // I want to avoid this duplication
}

I tried to use the code posted by Blaise Doughan:

private static class ToLowerCaseNamesStreamReaderDelegate extends StreamReaderDelegate {

    public ToLowerCaseNamesStreamReaderDelegate(XMLStreamReader xsr) {
        super(xsr);
    }

    @Override
    public String getAttributeLocalName(int index) {
        return super.getAttributeLocalName(index).toLowerCase();
    }

    @Override开发者_运维问答
    public String getLocalName() {
        return super.getLocalName().toLowerCase();
    }

}


@XmlRootElement(name="doc")
static class Doc {
    @XmlElement(name="element")
    List<Element> elements;
}

static class Element {
    @XmlAttribute(name = "abc")
    String abc;
}

public static void main(String[] args) throws Exception {
    XMLInputFactory xif = XMLInputFactory.newInstance();
    XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("LowerCaseElementNamesFilterTest.xml"));

    Unmarshaller u = JAXBContext.newInstance(Doc.class).createUnmarshaller();

    //Do unmarshalling
    Doc doc = (Doc) u.unmarshal(new ToLowerCaseNamesStreamReaderDelegate(xsr));

    System.out.println(doc.elements.get(0).abc);
    System.out.println(doc.elements.get(1).abc);
    System.out.println(doc.elements.get(2).abc);

}

This actually did not work.

null
2
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
    at java.util.ArrayList.RangeCheck(ArrayList.java:546)
    at java.util.ArrayList.get(ArrayList.java:321)
    at com.hre.commons.tec.xml.LowerCaseElementNamesFilter.main(LowerCaseElementNamesFilter.java:58)

For this XML:

<doc>
    <Element ABC="1"></Element>
    <element Abc="1"></element>
    <element abc="2"></element>
</doc>


You could map all your properties to lower case node names, and then wrap an XMLStreamReader to call toLowerCase() on all attribute/element names it returned. Then unmarshal from that XMLStreamReader.

I have recently added an enhance request for EclipseLink JAXB (MOXy) for this issue, feel free to provide additional information:

  • https://bugs.eclipse.org/bugs/show_bug.cgi?id=331241

Object Model

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private int id;
    private String name;
    private Address address;

    @XmlAttribute
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

}


public class Address {

    private String street;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

}

Demo Code

import java.io.FileInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml"));
        xsr = new MyStreamReaderDelegate(xsr);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Customer customer = (Customer) unmarshaller.unmarshal(xsr);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

    private static class MyStreamReaderDelegate extends StreamReaderDelegate {

        public MyStreamReaderDelegate(XMLStreamReader xsr) {
            super(xsr);
        }

        @Override
        public String getAttributeLocalName(int index) {
            return super.getAttributeLocalName(index).toLowerCase();
        }

        @Override
        public String getLocalName() {
            return super.getLocalName().toLowerCase();
        }

    }

}

Will read these XML documents:

<CUSTOMER ID="1">
    <NAME>Jane Doe</NAME>
    <ADDRESS>
        <STREET>123 A Street</STREET>
    </ADDRESS>
</CUSTOMER>

<CuSToMeR Id="1">
    <NaMe>Jane Doe</NAME>
    <AdDrEsS>
        <STREET>123 A Street</STREET>
    </AdDRrEsS>
</CuSToMeR>

And write the following XML:

<customer id="1">
   <address>
      <street>123 A Street</street>
   </address>
   <name>Jane Doe</name>
</customer>

Below is a link to a more detailed example:

  • http://bdoughan.blogspot.com/2010/12/case-insensitive-unmarshalling.html

UPDATE

Your code works in my environment (JDK 1.6.0_20 with both the included JAXB, and EclipseLink JAXB (MOXy) 2.2, I'm also using the default implementation of StAX). When I run your example:

import java.io.FileInputStream;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;

public class Example {

    private static class ToLowerCaseNamesStreamReaderDelegate extends StreamReaderDelegate {

        public ToLowerCaseNamesStreamReaderDelegate(XMLStreamReader xsr) {
            super(xsr);
        }

        @Override
        public String getAttributeLocalName(int index) {
            return super.getAttributeLocalName(index).toLowerCase();
        }

        @Override
        public String getLocalName() {
            return super.getLocalName().toLowerCase();
        }

    }


    @XmlRootElement(name="doc")
    static class Doc {
        @XmlElement(name="element")
        List<Element> elements;
    }

    static class Element {
        @XmlAttribute(name = "abc")
        String abc;
    }

    public static void main(String[] args) throws Exception {
        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("LowerCaseElementNamesFilterTest.xml"));

        Unmarshaller u = JAXBContext.newInstance(Doc.class).createUnmarshaller();

        //Do unmarshalling
        Doc doc = (Doc) u.unmarshal(new ToLowerCaseNamesStreamReaderDelegate(xsr));

        System.out.println(doc.elements.get(0).abc);
        System.out.println(doc.elements.get(1).abc);
        System.out.println(doc.elements.get(2).abc);

    }
}

I get the following output:

1
1
2

UPDATE #2

To address:

Exception in thread "main" javax.xml.bind.UnmarshalException - with linked exception: [javax.xml.bind.UnmarshalException: Namespace URIs and local names to the unmarshaller needs to be interned.] at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:425) at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:362) at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:332)

Have you tried modifying the delegate?:

@Override
public String getAttributeLocalName(int index) {
    return super.getAttributeLocalName(index).toLowerCase().intern();
}

@Override
public String getLocalName() {
    return super.getLocalName().toLowerCase().intern();
}


I am using moxy 2.7.1 version and it is working fine with UnmarshallerProperty as below.

unmarshaller.setProperty(UnmarshallerProperties.UNMARSHALLING_CASE_INSENSITIVE, true);

eclipselink documentation


Encounter the similar problem. My solution is to add @XmlAdpater on the model entity, which refers to your own XMLAdapter, this would give a flexible customization on the marshall/unmarshall.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜