Tagged: xml

Basic RESTful Webapp


Here comes a simple web application for demonstrating the RESTFul web APIs.

Source

svn

$ svn co http://jinahya.googlecode.com/svn/trunk\
> /com.googlecode.jinahya.test/basic-restful-webapp
...
Checked out revision n.

$

mvn

$ cd basic-restful-webapp
$ mvn clean package embedded-glassfish:run
...
Hit ENTER to redeploy, X to exit

Now you can access to resources under http://localhost:58080/basic-restful-webapp/.

Domain Objects

Item.java

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(namespace = "http://jinahya.googlecode.com/test")
@XmlType(propOrder = {"name", "stock"})
public class Item implements Serializable {


    private static final long serialVersionUID = -3104855615755133457L;


    private static final AtomicLong ATOMIC_ID = new AtomicLong();


    public static Item newInstance(final String name, final int stock) {

        final Item instance = new Item();

        instance.name = name;
        instance.stock = stock;

        return instance;
    }


    public static Item putCreatedAtAndId(final Item item) {

        item.createdAt = new Date();
        item.id = ATOMIC_ID.getAndIncrement();

        return item;
    }


    protected Item() {
        super();
    }


    @XmlAttribute
    public Date getCreatedAt() {
        return createdAt;
    }


    @XmlAttribute
    public Date getUpdatedAt() {
        return updatedAt;
    }


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


    private Date createdAt;


    protected Date updatedAt;


    private Long id;


    @XmlElement(required = true, nillable = true)
    protected String name;


    @XmlElement(required = true, nillable = true)
    protected int stock;


}

Items.java

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(namespace = "http://jinahya.googlecode.com/test")
public class Items implements Serializable {


    private static final long serialVersionUID = 5775071328874654134L;


    public Collection<Item> getItems() {

        if (items == null) {
            items = new ArrayList<>();
        }

        return items;
    }


    @XmlElement(name = "item")
    private Collection<Item> items;


}

Business Facade

ItemTable.java

public class ItemFacade {


    private static final long serialVersionUID = 5775071328874654134L;


    private static class InstanceHolder {


        private static final ItemFacade INSTANCE = new ItemFacade();


        private InstanceHolder() {
            super();
        }


    }


    public static ItemFacade getInstance() {
        return InstanceHolder.INSTANCE;
    }


    private ItemFacade() {
        super();
    }


    public void insert(final Item... items) {

        for (Item item : items) {
            Item.putCreatedAtAndId(item);
            tuples.put(item.getId(), item);
        }
    }


    public Item select(final long id) {

        return tuples.get(id);
    }


    public Collection<Item> selectAll() {
        return tuples.values();
    }


    public boolean update(final long id, final Item newItem) {

        if (!tuples.containsKey(id)) {
            return false;
        }

        final Item oldItem = tuples.get(id);
        if (oldItem == null) {
            return false;
        }

        oldItem.updatedAt = new Date();
        oldItem.name = newItem.name;
        oldItem.stock = newItem.stock;

        return true;
    }


    public Item delete(final long id) {
        return tuples.remove(id);
    }


    public void deleteAll() {
        tuples.clear();
    }


    private final Map<Long, Item> tuples = new HashMap<>();


}

Webservice Resource

ItemsResource.java

@Path("/items")
public class ItemsResource {


    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Items read() {

        final Items items = new Items();

        items.getItems().addAll(ItemFacade.getInstance().selectAll());

        return items;
    }


    @DELETE
    public void delete() {

        ItemFacade.getInstance().deleteAll();
    }


    @POST
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response createItem(@Context final UriInfo info,
                               final Item item) {

        ItemFacade.getInstance().insert(item);

        UriBuilder builder = info.getAbsolutePathBuilder();
        builder = builder.path(Long.toString(item.getId()));

        final URI uri = builder.build();

        return Response.created(uri).build();
    }


    @Path("/{id: \\d+}")
    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Item readItem(@PathParam("id") final long id) {

        return ItemFacade.getInstance().select(id);
    }


    @Path("/{id: \\d+}")
    @PUT
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response updateItem(@PathParam("id") final long id,
                               final Item newItem) {

        final boolean updated = ItemFacade.getInstance().update(id, newItem);

        if (!updated) {
            return Response.status(Status.NOT_FOUND).build();
        }

        return Response.status(Status.NO_CONTENT).build();
    }


    @Path("/{id: \\d+}")
    @DELETE
    public void deleteItem(@PathParam("id") final long id) {

        ItemFacade.getInstance().delete(id);
    }


}
resource POST GET PUT DELETE
/items CREATE
/items/{id: \\d+} READ UPDATE DELETE

Demonstration

CREATE

$ cat src/test/resources/item.xml
<item xmlns="http://jinahya.googlecode.com/test">
  <name>xml</name>
  <stock>30</stock>
</item>

$ curl -i \
> -X POST \
> http://localhost:58080/items \
> -H "Content-Type: application/xml" \
> --data "@src/test/resources/item.xml"
HTTP/1.1 201 Created
X-Powered-By: ...
Server: ...
Location: http://localhost:58080/items/0
Content-Length: 0
Date: ...

$

READ

$ curl -s \
> http://localhost:58080/items/0 \
> -H "Accept: application/xml" \
> | xmllint --format -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:item xmlns:ns2="http://jinahya.googlecode.com/test"
          createdAt="2013-03-29T17:43:12.413+09:00"
          id="0">
  <name>xml</name>
  <stock>30</stock>
</ns2:item>

$

UPDATE

$ cat src/test/resources/item.json
{
    "name":"json",
    "stock":"40"
}

$ curl -i \
> -X PUT \
> http://localhost:58080/items/0 \
> -H "Content-Type: application/json" \
> --data "@src/test/resources/item.json"
HTTP/1.1 204 No Content
X-Powered-By: ...
Server: ...
Date: ...


$ curl -s \
> http://localhost:58080/items/0 \
> -H "Accept: application/json" \
> | python -m json.tool
{
    "@createdAt": "2013-03-29T17:43:12.413+09:00",
    "@id": "0",
    "@updatedAt": "2013-03-29T17:48:23.238+09:00",
    "name": "json",
    "stock": "40"
}

$

DELETE

$ curl -i \
> -X DELETE \
> http://localhost:58080/items/0
HTTP/1.1 204 No Content
X-Powered-By: ...
Server: ...
Date: ...

$

/schema.xsd

$ curl -s http://localhost:58080/schema.xsd | xmllint --format -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema xmlns:tns="http://jinahya.googlecode.com/test"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           attributeFormDefault="unqualified"
           elementFormDefault="qualified"
           version="1.0"
           targetNamespace="http://jinahya.googlecode.com/test">
  <xs:element name="imageFormat" type="tns:imageFormat"/>
  <xs:element name="imageFormats" type="tns:imageFormats"/>
  <xs:element name="imageSuffix" type="tns:imageSuffix"/>
  <xs:element name="imageSuffixes" type="tns:imageSuffixes"/>
  <xs:element name="item" type="tns:item"/>
  <xs:element name="items" type="tns:items"/>
  <xs:complexType name="imageFormat">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="canRead" type="xs:boolean" use="required"/>
        <xs:attribute name="canWrite" type="xs:boolean" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="imageFormats">
    <xs:sequence>
      <xs:any processContents="lax" namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="empty" type="xs:boolean"/>
  </xs:complexType>
  <xs:complexType name="imageSuffix">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="canRead" type="xs:boolean" use="required"/>
        <xs:attribute name="canWrite" type="xs:boolean" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="imageSuffixes">
    <xs:sequence>
      <xs:any processContents="lax" namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="empty" type="xs:boolean"/>
  </xs:complexType>
  <xs:complexType name="item">
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="stock" type="xs:int"/>
    </xs:sequence>
    <xs:attribute name="createdAt" type="xs:dateTime"/>
    <xs:attribute name="id" type="xs:long"/>
    <xs:attribute name="updatedAt" type="xs:dateTime"/>
  </xs:complexType>
  <xs:complexType name="items">
    <xs:sequence>
      <xs:element ref="tns:item" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="empty" type="xs:boolean"/>
  </xs:complexType>
</xs:schema>

/imageFormats

POST GET PUT DELETE
/imageFormats Read All
/imageTypes/{name} Read Single
$ curl -s -H "Accept: application/xml" http://localhost:58080/imageFormats
<imageFormats xmlns="http://jinahya.googlecode.com/test">
  <imageFormat canRead="true" canWrite="true">jpg</imageFormat>
  <imageFormat canRead="true" canWrite="true">bmp</imageFormat>
  <imageFormat canRead="true" canWrite="true">BMP</imageFormat>
  <imageFormat canRead="true" canWrite="true">JPG</imageFormat>
  <imageFormat canRead="true" canWrite="true">wbmp</imageFormat>
  <imageFormat canRead="true" canWrite="true">jpeg</imageFormat>
  <imageFormat canRead="true" canWrite="true">png</imageFormat>
  <imageFormat canRead="true" canWrite="true">PNG</imageFormat>
  <imageFormat canRead="true" canWrite="true">JPEG</imageFormat>
  <imageFormat canRead="true" canWrite="true">WBMP</imageFormat>
  <imageFormat canRead="true" canWrite="true">GIF</imageFormat>
  <imageFormat canRead="true" canWrite="true">gif</imageFormat>
</imageFormats>
$ curl -s -H "Accept: application/json" http://localhost:58080/imageFormats
{
    "imageFormat": [
        {
            "$": "bmp",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "BMP",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "jpg",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "JPG",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "wbmp",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "jpeg",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "png",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "PNG",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "JPEG",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "WBMP",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "GIF",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "gif",
            "@canRead": "true",
            "@canWrite": "true"
        }
    ]
}
$ curl -s -H "Accept: application/xml" http://localhost:58080/imageFormats/png
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imageFormat xmlns="http://jinahya.googlecode.com/test"
             canRead="true" canWrite="true">png</imageFormat>
$ curl -s -H "Accept: application/json" http://localhost:58080/imageFormats/jpeg
{
    "$": "jpeg",
    "@canRead": "true",
    "@canWrite": "true"
}

/imageTypes

POST GET PUT DELETE
/imageTypes Read All
/imageTypes/{name} Read Single
$ curl -s -H "Accept: application/xml" http://localhost:58080/imageTypes
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imageTypes xmlns="http://jinahya.googlecode.com/test">
  <imageType canRead="true" canWrite="true">image/png</imageType>
  <imageType canRead="true" canWrite="true">image/jpeg</imageType>
  <imageType canRead="true" canWrite="true">image/x-png</imageType>
  <imageType canRead="true" canWrite="true">image/vnd.wap.wbmp</imageType>
  <imageType canRead="true" canWrite="true">image/bmp</imageType>
  <imageType canRead="true" canWrite="true">image/gif</imageType>
</imageTypes>
$ curl -s -H "Accept: application/json" http://localhost:58080/imageTypes
{
    "imageType": [
        {
            "$": "image/png",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "image/jpeg",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "image/x-png",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "image/vnd.wap.wbmp",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "image/bmp",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "image/gif",
            "@canRead": "true",
            "@canWrite": "true"
        }
    ]
}
$ curl -s -H "Accept: application/xml" http://localhost:58080/imageTypes/image/png
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imageType xmlns="http://jinahya.googlecode.com/test"
           canRead="true" canWrite="true">image/png</imageType>
$ curl -s -H "Accept: application/json" http://localhost:58080/imageTypes/image/jpeg
{
    "$": "image/jpeg",
    "@canRead": "true",
    "@canWrite": "true"
}

/imageSuffixes

POST GET PUT DELETE
/imageTypes Read All
/imageTypes/{name} Read Single
$ curl -H "Accept: application/xml" http://localhost:58080/imageSuffixes
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imageSuffixes xmlns="http://jinahya.googlecode.com/test">
  <imageSuffix canRead="true" canWrite="true">bmp</imageSuffix>
  <imageSuffix canRead="true" canWrite="true">jpg</imageSuffix>
  <imageSuffix canRead="true" canWrite="true">jpeg</imageSuffix>
  <imageSuffix canRead="true" canWrite="true">wbmp</imageSuffix>
  <imageSuffix canRead="true" canWrite="true">png</imageSuffix>
  <imageSuffix canRead="true" canWrite="true">gif</imageSuffix>
</imageSuffixes>
$ curl -H "Accept: application/json" http://localhost:58080/imageSuffixes
{
    "imageSuffix": [
        {
            "$": "bmp",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "jpg",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "wbmp",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "jpeg",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "png",
            "@canRead": "true",
            "@canWrite": "true"
        },
        {
            "$": "gif",
            "@canRead": "true",
            "@canWrite": "true"
        }
    ]
}
$ curl -H "Accept: application/xml" http://localhost:58080/imageSuffixes/png
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imageSuffix xmlns="http://jinahya.googlecode.com/test"
             canRead="true" canWrite="true">png</imageSuffix>
$ curl -s -H "Accept: application/json" http://localhost:58080/imageSuffixes/jpeg
{
    "$": "jpeg",
    "@canRead": "true",
    "@canWrite": "true"
}

Serving XML Schema with JAX-RS


update

@GET
@Path("/some.xsd")
@Produces({MediaType.APPLICATION_XML})
public Response readSomeXsd() throws JAXBException, IOException {

    final JAXBContext context; // get some

    return Response.ok((StreamingOutput) output -> {
        context.generateSchema(new SchemaOutputResolver() {
            @Override
            public Result createOutput(final String namespaceUri,
                                       final String suggestedFileName)
                throws IOException {
                final Result result = new StreamResult(output);
                result.setSystemId(uriInfo.getAbsolutePath().toString());
                return result;
            }
        });
    }).build();
}

@Context
private transient UriInfo uriInfo;

You can serve the XML Schema for your domain objects.

public class SchemaStreamingOutput implements StreamingOutput {

    public SchemaStreamingOutput(final JAXBContext context) {
        super();

        if (context == null) {
            throw new NullPointerException("null context");
        }

        this.context = context;
    }

    @Override
    public void write(final OutputStream output) throws IOException {

        context.generateSchema(new SchemaOutputResolver() {

            @Override
            public Result createOutput(final String namespaceUri,
                                       final String suggestedFileName)
                throws IOException {

                return new StreamResult(output) {

                    @Override
                    public String getSystemId() { // nasty
                        return suggestedFileName;
                    }
                };
            }
        });
    }

    private final JAXBContext context;
}

Now you can serve your XML Schema like this.

@GET
@Path("/items.xsd")
@Produces({MediaType.APPLICATION_XML})
public Response readXsd() throws JAXBException {

    final JAXBContext context = ...;

    return Response.ok(new SchemaStreamingOutput(context)).build();
}

인터넷 우체국 사업자 포털 오픈API


인터넷에서 우편번호 검색하는 서비스가 있나 하고 검색을 하던 중 반가운 페이지에 도착했다.

이름하여, “오픈API“!!!

아… 이제 우리나라도 이런거 되는구나! 역시!!!

기쁨은 아주 잠시 뿐, 실망스러운 부분이 너무도 많다.

References


다음 링크들을 참고하였다.

HTTP

  • GET POST 둘 다 된다. (이건 좋은 건가?)
  • query request parameter는 인코딩을 아예 안하거나(@@?) euc-kr로 하면 된다.
  • Accept-Language: ko를 꼭 써줘야 한다.

XML

  • 오랜만에 보는 encoding="euc-kr"이다.
  • 아주 멋찐 <![CDATA[]]>!!!
  • 역시나 XML Namespace를 기대하기는 어렵다.
  • 특정 element localname({}postcd)이 예시문서({}zipcode)와 다르다.
  • 서울 광진구 구의1동 248~252 이 따위로 주면 번지 구간을 어떻게 자르라는 건가?

How should be

EJB, JAX-RS

여기에 Stateless Session Bean과 JAX-RS Resource들을 구현해 놓았다. 참고하시길.
단 PostalCodesBean을 쓰려면 다음과 같이 ejb-jar.xml에 regkey값을 설정해줘야 한다.

<?xml version="1.0" encoding="UTF-8"?>

<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
         version = "3.1"
         xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
  <enterprise-beans>
    <session>
      <ejb-name>PostalCodesBean</ejb-name>
      <env-entry>
        <env-entry-name>com.googlecode.jinahya.epost.openapi.PostalCodesBean/regkey</env-entry-name>
        <!--env-entry-type>java.lang.String</env-entry-type-->
        <env-entry-value>{your_regkey}</env-entry-value>
      </env-entry>
    </session>
  </enterprise-beans>
</ejb-jar>

XML Schema

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" version="1.0"
           targetNamespace="http://jinahya.googlecode.com/epost/openapi"
           xmlns="http://jinahya.googlecode.com/epost/openapi"
           xmlns:tns="http://jinahya.googlecode.com/epost/openapi"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <xs:element name="postalCodes" type="postalCodes"/>

  <xs:complexType name="postalCode">
    <xs:sequence>
      <xs:element name="address" type="xs:string"/>
      <xs:element name="code" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="postalCodes">
    <xs:sequence>
      <xs:element name="postalCode" type="postalCode" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

XML

<?xml version="1.0" encoding="utf-8"?>
<postalCodes xmlns:xs="http://www.w3.org/2001/XMLSchema"
	     xmlns="http://jinahya.googlecode.com/epost/openapi"
	     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <postalCode>
    <address>서울 광진구 구의1동 632~640</address>
    <code>143835</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 257</address>
    <code>143828</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 253~254</address>
    <code>143827</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 248~252</address>
    <code>143826</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 232~241</address>
    <code>143824</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 221~222</address>
    <code>143823</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 서울광진경찰서</address>
    <code>143703</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동</address>
    <code>143201</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 새한아파트</address>
    <code>143722</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 242~243</address>
    <code>143825</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 246</address>
    <code>143825</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 641~655</address>
    <code>143836</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 크레신타워3차</address>
    <code>143716</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 225~226</address>
    <code>143960</code>
  </postalCode>
  <postalCode>
    <address>서울 광진구 구의1동 229~231</address>
    <code>143962</code>
  </postalCode>
</postalCodes>

Comments

<bluff>지금 내가 가는 길을 누군가 먼저 걷지 않았다면 내 발은 온통 자갈에 채여 멍이 들고 가시에 찔려 피를 흘리고 있을 것이다.</bluff>

— 나 —

아래는 그 ‘누군가’에 해당하는 분들의 말씀들이다.

어지간히 단가가 쌌던 모양이다.

— Dopany Soft —

여담이지만 우리나라 API제공은 전혀 친철하지 않다..
그냥 알아서 쓸려면 쓰고 말라면 말라는 식이다.

우체국 API페이지도 들어가보면 참 부실하기 짝이 없는 도움말들..
뭐 이유야 훤히 보이는 거지만..
외부에 맞겨서 API개발하고 소스는 돌아가지만
그 시스템을 아는 사람은 우체국 쪽에 별로 없다..
그래서 도움말도 외부업체에서 받은 기술서가 전부고 그마저도 대충 보여주고 마는 거겠지…

— 수유산장 —

XML Schema strings with JAXB


References

package com.googlecode.jinahya.test;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
import javax.xml.bind.annotation.adapters.NormalizedStringAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;

/**
 *
 * @author Jin Kwon <jinahya at gmail.com>
 */
@XmlRootElement(name = "bind")
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(propOrder = {"token", "norma", "strin"})
public class StringBind {

    public static void main(final String[] args)
        throws JAXBException, IOException {

        final JAXBContext context = JAXBContext.newInstance(StringBind.class);

        // XML Schema ----------------------------------------------------------
        final StringWriter schemaWriter = new StringWriter();
        context.generateSchema(new SchemaOutputResolver() {
            @Override
            public Result createOutput(final String namespaceUri,
                                       final String suggestedFileName)
                throws IOException {
                return new StreamResult(schemaWriter) {
                    @Override
                    public String getSystemId() {
                        return suggestedFileName;
                    }
                };
            }
        });
        schemaWriter.flush();
        System.out.println("-------------------------------------- XML Schema");
        System.out.println(schemaWriter.toString());

        final String unprocessed = "\t ab\r\n   c\t \r \n";

        // marshal -------------------------------------------------------------
        final StringBind marshalling = new StringBind();
        marshalling.token = unprocessed;
        marshalling.norma = unprocessed;
        marshalling.strin = unprocessed;
        final Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        final StringWriter writer = new StringWriter();
        marshaller.marshal(marshalling, writer);
        writer.flush();
        System.out.println("-------------------------------------- marshalled");
        System.out.println(writer.toString());

        // unmarshal -----------------------------------------------------------
        final Unmarshaller unmarshaller = context.createUnmarshaller();
        final StringBind unmarshalled = (StringBind) unmarshaller.unmarshal(
            new StringReader(writer.toString()));
        System.out.println("------------------------------------ unmarshalled");
        System.out.println(unmarshalled.toString());
    }

    @Override
    public String toString() {
        return "token: " + token + "\nnorma: " + norma + "\nstrin: " + strin;
    }

    @XmlElement
    @XmlSchemaType(name = "token")
    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
    private String token;

    @XmlElement
    @XmlSchemaType(name = "normalizedString")
    @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
    private String norma;

    @XmlElement
    //@XmlSchemaType(name = "string") // @@?
    //@XmlJavaTypeAdapter(StringAdapter.class) // there is no StringAdapter.class
    private String strin;
}

Note that NormalizedStringAdapter#marshal(String) and CollapsedStringAdapter#marshal(String) don’t do anything.

-------------------------------------- XML Schema
<?xml version="1.0" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="bind" type="stringBind"/>

  <xs:complexType name="stringBind">
    <xs:sequence>
      <xs:element name="token" type="xs:token" minOccurs="0"/>
      <xs:element name="norma" type="xs:normalizedString" minOccurs="0"/>
      <xs:element name="strin" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>


-------------------------------------- marshalled
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bind>
    <token>	 ab
   c	  
</token>
    <norma>	 ab
   c	  
</norma>
    <strin>	 ab
   c	  
</strin>
</bind>

------------------------------------ unmarshalled
token: ab c
norma:   ab    c    
strin: 	 ab
   c