Tagged: xml

Inserting an XML declaration with nXML mode


references

  • how to?

    Just do

    M-x nxml-insert-xml-declaration
    

    .
    Or, shortly, do

    C-c C-x
    

    .

    Advertisements
  • 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