簡體   English   中英

JAXB / Jersey-JSONObject作為XmlElement

[英]JAXB / Jersey - JSONObject as XmlElement

我正在使用Jersey(1.9.1),並將JAXB用於我的對象,並嘗試添加一個只是JSON對象的字段,理想情況下,該字段的類型為Jackson的JsonNode,但我也可以使用jettison的JSONObject(甚至GSON的JsonObject) / JsonElement)

這樣做的原因是因為除了同一個對象上的其他元素(使用JAXB注釋)之外,我還希望發送“自由格式”的json(作為示例中的obj元素)。

我的對象:

import org.codehaus.jettison.json.JSONObject;

@XmlRootElement(name="moo")
@XmlAccessorType(XmlAccessType.FIELD)

public class Moo {

    @XmlElement
    JSONObject obj;

    ...
}

我的資源:

@Path("/moo")
@POST
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public Response postMsg(Moo e) {
    System.out.println(e);

    return Response.status(200).entity(e).build();
}

有效負載:

{"obj": {"my": "custom", "object": 1, "here": [1, 2, 3] } }

當執行上述POST請求時, obj是一個空的JSONObject 當我嘗試使用Jackson的JsonNode和GSON的JsonElement / JsonObject時,發生了同樣的事情。

但是,當我使用JSONObject作為方法的arg時,它能夠解析它

@Path("/moo")
@POST
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public Response postMsg(JSONObject e) {
    System.out.println(e);

    return Response.status(200).entity(e).build();
}

我認為發生這種情況是因為Jersey使用其自己的com.sun.jersey.json.impl.provider.entity.JSONObjectProvider進行編組,而JAXB使用其自己的編組器。

在仍然使用JAXB的注釋(針對其他字段)時,是否有解決方法? 我嘗試使用自定義XmlAdapter (使用@XmlJavaTypeAdapter批注),但沒有成功(因為作為ValueType獲得的對象是DOM元素)

我的最終目標是將字段obj作為Jackson表示形式( JsonNode )。

任何方向將不勝感激。

如果我做對了,您想嵌入一個原始json以獲取自由值。

@XmlRootElement(name="moo")
@XmlAccessorType(XmlAccessType.FIELD)
public class Moo {
    Object json;

    @JsonRawValue
    public String getJson() {
        // default raw value: null or "[]"
        return json == null ? null : json.toString();
    }

    public void setJson(JsonNode node) {
        this.json = node;
    }
}

取自這里

首先有很多問題

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
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.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.validation.Schema;
import javax.xml.validation.TypeInfoProvider;
import javax.xml.validation.Validator;
import javax.xml.validation.ValidatorHandler;
import java.io.File;
import java.io.IOException;
import java.io.Writer;

public class TestObjectNode {

    @XmlRootElement(name = "moo")
    @XmlAccessorType(XmlAccessType.FIELD)
    static class Moo {
        @JsonProperty("obj")
        @XmlElement
        @XmlJavaTypeAdapter(ObjMarshaller.class)
        JsonNode obj;
    }

    static final ObjectMapper JSON_MAPPER = new ObjectMapper();
    static final XmlMapper XML_MAPPER = (XmlMapper) new XmlMapper()
            .disable(SerializationFeature.WRAP_ROOT_VALUE)
            .disable(DeserializationFeature.UNWRAP_ROOT_VALUE);

    public static void main(String[] args) throws Exception {
        final String value = "{\"obj\": {\"my\": \"custom\", \"object\": 1, \"here\": [1, 2, 3] } }";
        Moo moo = JSON_MAPPER.readValue(value, Moo.class);
        System.out.println(moo.obj);
        saveToFile(moo);
        System.out.println(XML_MAPPER.writeValueAsString(moo));
        moo = readFromFile();
        System.out.println(moo.obj);
    }

    private static void saveToFile(Moo moo) throws Exception {
        File file = new File("moo.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.setProperty(CharacterEscapeHandler.class.getName(),
                new CharacterEscapeHandler() {
                    @Override
                    public void escape(char[] chars, int i, int i1, boolean b, Writer writer) throws IOException {
                        writer.write(chars, i, i1);
                    }
                });

        jaxbMarshaller.marshal(moo, file);
        jaxbMarshaller.marshal(moo, System.out);
    }

    private static Moo readFromFile() throws Exception {
        File file = new File("moo.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        MySchema mySchema = new MySchema();
        unmarshaller.setSchema(mySchema);
        Moo moo = (Moo) unmarshaller.unmarshal(file);
        String objectNode = mySchema.validatorHandler.getObjectNodeString();
        System.out.println("objectNode = " + objectNode);
        JsonNode jsonNode = XML_MAPPER.readValue(objectNode, ObjectNode.class);
        moo.obj = jsonNode;
        return moo;
    }

    private static class ObjMarshaller extends XmlAdapter<String, JsonNode> {

        @Override
        public JsonNode unmarshal(String v) throws Exception {
            throw new IllegalArgumentException("i'm never here///");
        }

        @Override
        public String marshal(JsonNode v) throws Exception {
            return XML_MAPPER.writeValueAsString(v);
        }
    }

    static class MySchema extends Schema {

        final ValidatorHandlerImpl validatorHandler = new ValidatorHandlerImpl();

        @Override
        public Validator newValidator() {
            return null;
        }

        @Override
        public ValidatorHandler newValidatorHandler() {
            return validatorHandler;
        }
    }

    static class ValidatorHandlerImpl extends ValidatorHandler {

        private final StringBuilder objectNode = new StringBuilder();
        private boolean insideObjectNode = false;

        public String getObjectNodeString() {
            return objectNode.toString();
        }

        @Override
        public void setContentHandler(ContentHandler receiver) {

        }

        @Override
        public ContentHandler getContentHandler() {
            return null;
        }

        @Override
        public void setErrorHandler(ErrorHandler errorHandler) {

        }

        @Override
        public ErrorHandler getErrorHandler() {
            return null;
        }

        @Override
        public void setResourceResolver(LSResourceResolver resourceResolver) {

        }

        @Override
        public LSResourceResolver getResourceResolver() {
            return null;
        }

        @Override
        public TypeInfoProvider getTypeInfoProvider() {
            return null;
        }

        @Override
        public void setDocumentLocator(Locator locator) {

        }

        @Override
        public void startDocument() throws SAXException {

        }

        @Override
        public void endDocument() throws SAXException {

        }

        @Override
        public void startPrefixMapping(String prefix, String uri) throws SAXException {

        }

        @Override
        public void endPrefixMapping(String prefix) throws SAXException {

        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
            if(localName.equals("ObjectNode")) {
                insideObjectNode = true;
            }
            if (insideObjectNode) {
                objectNode.append("<" + localName + ">");
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (insideObjectNode) {
                objectNode.append("</" + localName + ">");
            }
            if(localName.equals("ObjectNode")) {
                insideObjectNode = false;
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (insideObjectNode) {
                objectNode.append(ch, start, length);
            }
        }

        @Override
        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {

        }

        @Override
        public void processingInstruction(String target, String data) throws SAXException {

        }

        @Override
        public void skippedEntity(String name) throws SAXException {

        }
    }

}

提供以下輸出

{"my":"custom","object":1,"here":[1,2,3]}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<moo>
    <obj><ObjectNode><my>custom</my><object>1</object><here>1</here><here>2</here><here>3</here></ObjectNode></obj>
</moo>
<Moo><obj><my>custom</my><object>1</object><here>1</here><here>2</here><here>3</here></obj></Moo>
objectNode = <ObjectNode><my>custom</my><object>1</object><here>1</here><here>2</here><here>3</here></ObjectNode>
{"my":"custom","object":"1","here":"3"}

因此,不幸的是,這是因為Jackson XML無法將“ here”解析為數組。 因此,我們應該以不同的方式對其進行序列化:

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
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.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.validation.Schema;
import javax.xml.validation.TypeInfoProvider;
import javax.xml.validation.Validator;
import javax.xml.validation.ValidatorHandler;
import java.io.File;
import java.io.IOException;
import java.io.Writer;

public class TestObjectNode {

    @XmlRootElement(name = "moo")
    @XmlAccessorType(XmlAccessType.FIELD)
    static class Moo {
        @JsonProperty("obj")
        @XmlElement
        @XmlJavaTypeAdapter(ObjMarshaller.class)
        JsonNode obj;
    }

    static final ObjectMapper JSON_MAPPER = new ObjectMapper();
    static final XmlMapper XML_MAPPER = (XmlMapper) new XmlMapper()
            .disable(SerializationFeature.WRAP_ROOT_VALUE)
            .disable(DeserializationFeature.UNWRAP_ROOT_VALUE);

    public static void main(String[] args) throws Exception {
        final String value = "{\"obj\": {\"my\": \"custom\", \"object\": 1, \"here\": [1, 2, 3] } }";
        Moo moo = JSON_MAPPER.readValue(value, Moo.class);
        System.out.println(moo.obj);
        saveToFile(moo);
        System.out.println(XML_MAPPER.writeValueAsString(moo));
        moo = readFromFile();
        System.out.println(moo.obj);
    }

    private static void saveToFile(Moo moo) throws Exception {
        File file = new File("moo.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.setProperty(CharacterEscapeHandler.class.getName(),
                new CharacterEscapeHandler() {
                    @Override
                    public void escape(char[] chars, int i, int i1, boolean b, Writer writer) throws IOException {
                        writer.write(chars, i, i1);
                    }
                });

        jaxbMarshaller.marshal(moo, file);
        jaxbMarshaller.marshal(moo, System.out);
    }

    private static Moo readFromFile() throws Exception {
        File file = new File("moo.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        MySchema mySchema = new MySchema();
        unmarshaller.setSchema(mySchema);
        Moo moo = (Moo) unmarshaller.unmarshal(file);
        String objectNode = mySchema.validatorHandler.getObjectNodeString();
        System.out.println("objectNode = " + objectNode);
        JsonNode jsonNode = XML_MAPPER.readValue(objectNode, ObjectNode.class);
        Object hereObject = jsonNode.get("here");
        if (hereObject instanceof TextNode) {
            String hereArray = ((TextNode) hereObject).asText().replace("<![CDATA[", "").replace("]]>", "");
            ArrayNode here = JSON_MAPPER.readValue(hereArray, ArrayNode.class);
            ((ObjectNode) jsonNode).put("here", here);
        }
        moo.obj = jsonNode;
        return moo;
    }

    private static class ObjMarshaller extends XmlAdapter<String, JsonNode> {

        @Override
        public JsonNode unmarshal(String v) throws Exception {
            return null;
        }

        @Override
        public String marshal(JsonNode v) throws Exception {
            Object hereObject = v.get("here");
            if (hereObject instanceof ArrayNode) {
                ArrayNode here = (ArrayNode) hereObject;
                final StringBuilder value = new StringBuilder("<![CDATA[").append('[');
                for (int i = 0; i < here.size(); ++i) {
                    if (i > 0) {
                        value.append(',');
                    }
                    value.append(here.get(i));
                }
                value.append(']').append("]]>");
                ((ObjectNode) v).put("here", value.toString());
            }
            return XML_MAPPER.writeValueAsString(v);
        }
    }

    static class MySchema extends Schema {

        final ValidatorHandlerImpl validatorHandler = new ValidatorHandlerImpl();

        @Override
        public Validator newValidator() {
            return null;
        }

        @Override
        public ValidatorHandler newValidatorHandler() {
            return validatorHandler;
        }
    }

    static class ValidatorHandlerImpl extends ValidatorHandler {

        private final StringBuilder objectNode = new StringBuilder();
        private boolean insideObjectNode = false;

        public String getObjectNodeString() {
            return objectNode.toString();
        }

        @Override
        public void setContentHandler(ContentHandler receiver) {

        }

        @Override
        public ContentHandler getContentHandler() {
            return null;
        }

        @Override
        public void setErrorHandler(ErrorHandler errorHandler) {

        }

        @Override
        public ErrorHandler getErrorHandler() {
            return null;
        }

        @Override
        public void setResourceResolver(LSResourceResolver resourceResolver) {

        }

        @Override
        public LSResourceResolver getResourceResolver() {
            return null;
        }

        @Override
        public TypeInfoProvider getTypeInfoProvider() {
            return null;
        }

        @Override
        public void setDocumentLocator(Locator locator) {

        }

        @Override
        public void startDocument() throws SAXException {

        }

        @Override
        public void endDocument() throws SAXException {

        }

        @Override
        public void startPrefixMapping(String prefix, String uri) throws SAXException {

        }

        @Override
        public void endPrefixMapping(String prefix) throws SAXException {

        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
            if(localName.equals("ObjectNode")) {
                insideObjectNode = true;
            }
            if (insideObjectNode) {
                objectNode.append("<" + localName + ">");
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (insideObjectNode) {
                objectNode.append("</" + localName + ">");
            }
            if(localName.equals("ObjectNode")) {
                insideObjectNode = false;
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (insideObjectNode) {
                objectNode.append(ch, start, length);
            }
        }

        @Override
        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {

        }

        @Override
        public void processingInstruction(String target, String data) throws SAXException {

        }

        @Override
        public void skippedEntity(String name) throws SAXException {

        }
    }

}

結果:

{"my":"custom","object":1,"here":[1,2,3]}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<moo>
    <obj><ObjectNode><my>custom</my><object>1</object><here>&lt;![CDATA[[1,2,3]]]&gt;</here></ObjectNode></obj>
</moo>
<Moo><obj><my>custom</my><object>1</object><here>&lt;![CDATA[[1,2,3]]]&gt;</here></obj></Moo>
objectNode = <ObjectNode><my>custom</my><object>1</object><here><![CDATA[[1,2,3]]]></here></ObjectNode>
{"my":"custom","object":"1","here":[1,2,3]}

這僅是示例,說明如何精確地完成任務。 您會看到它需要一些硬編碼,因此最好考慮另一種方法。 也許@XmlAnyElement可以幫助您。 或使用先前答案的文字變體。 另外,如果刪除CharacterEscapeHandler.class,則會收到轉義的xml字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM