【问题标题】:Jersey: marsheling and unmarsheling xml within other xmlJersey:在其他 xml 中编组和解组 xml
【发布时间】:2013-12-26 20:12:52
【问题描述】:

简述问题的本质:

1) 如何在 Jersey 的其他 xml 中将一个应该由 CDATA 包装的 xml 进行双关?

2) 如何避免将"转为"<"">"转为"&gt ;" 在编组期间?

更多详情:

我需要将 XML 发送到外部服务器

此 XML 应采用以下格式:

<root>
   <element1>some value</element1>
   <element2>
      <![CDATA[<?xml version="1.0" encoding="UTF-8" ?>
      <innerXlmElement>
         <item>
            <itemElement1>1</itemElement1>
            <itemElement2>1</itemElement2>
         </item>
         ...
         <item>
            <itemElement1>n</itemElement1>
            <itemElement2>n</itemElement2>
         </item>
      </innerXlmElement>]]>
   </element2>
</root>

即外部 XML 内部的元素之一应该是另一个 XML,由 CDATA 包装。

首先我做了以下bean:

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

@XmlRootElement(name = "root")
public class Req {

   protected String element1;
   protected Element2 element2;

   @XmlElement(name = "element1")
   public String getElement1() {
      return element1;
   }

   public void setElement1(String element1) {
      this.element1 = element1;
   }

   @XmlElement(name = "element2")
   public Element2 geElement2() {
      return element2;
   }

   public void setElement2(Element2 element2) {
      this.element2 = element2;
   }

   public static class Element2{

   protected InnerXlmElement innerXlmElement;

   @XmlElement(name = "innerXlmElement")
   public InnerXlmElement getInnerXlmElement() {
      return innerXlmElement;
   }

   public void setInnerXlmElement(InnerXlmElement innerXlmElement) {
      this.innerXlmElement = innerXlmElement;
   }

   public static class InnerXlmElement{
      protected Item[] item;

   @XmlElement(name = "item")
   public Item[] getItem() {
      return item;
   }

   public void setItem(Item[] item) {
      this.item = item;
   }

   public static class Item{

      protected String itemElement1;
      protected String itemElement2;

      @XmlElement(name = "itemElement1")
      public String getItemElement1() {
         return itemElement1;
      }

      public void setItemElement1(String itemElement1) {
         this.svcId = itemElement1;
      }

      @XmlElement(name = "itemElement2")
      public String getItemElement2() {
         return itemElement2;
      }

      public void setItemElement2(String itemElement2) {
         this.itemElement2 = itemElement2;
      }
   }
}

生成的 XML,在请求期间已发送到外部服务器,如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
   <element1>3324</element1>
   <element2>
      <innerXlmElement>
         <item>
            <itemElement1>1</itemElement1>
            <itemElement2>2</itemElement2>
         </item>
      </innerXlmElement>
   </element2>
</root>

当然,生成的内部 XML 不是由 CDATA 包装的,它没有 第一个标记。

那么,问题是 CDATA 如何用 Jersey 包装 XML 元素?

另外,我试图粗鲁地解决这个问题。我手工制作了内部 XML,并把它放到像 String 这样的“Req”bean 中:

Req req = new Req();
req.setElement1("some value");
StringBuilder element2= new StringBuilder();
xmlServiceInfo.append("<![CDATA[<?xml version=\"1.0\" encoding=\"UTF-8\" ?><innerXlmElement><item><itemElement1>1</itemElement1><itemElement2>2</itemElement2></item></innerXlmElement>]]>");
req.setElement1(element2.toString());
...

在那个操作之后,我有了这样的 XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
   <element1>some value</element1>
   <element2>
      &lt;![CDATA[&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;&lt;innerXlmElement&gt;&lt;item&gt;&lt;itemElement1&gt;1&lt;/itemElement1&gt;&lt;itemElement1&gt;2&lt;/itemElement2&gt;&lt;/item&gt;&lt;/innerXlmElement&gt;]]&gt;
   </element2>
</root>

谁能告诉我,如何避免将 " 转换为 "& lt;"">" 转换为 "& gt;" 在编组期间?

--------- 更新 (14.01.14) ----------

我试过"JAXB use String as it is" solution

所以,首先我将 element2 的类型更改为 String 并在我的请求 bean 中添加了 @XmlAnyElement 注释:

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

@XmlRootElement(name = "root")
public class Req {

   protected String element1;
   protected String element2;

   @XmlElement(name = "element1")
   public String getElement1() {
      return element1;
   }

   public void setElement1(String element1) {
      this.element1 = element1;
   }

   @XmlAnyElement(value=CDATAHandler.class)
   public String getElement2() {
      return element2;
   }

   public void setElement2(String element2) {
      this.element2 = element2;
   }

然后我自己实现了 DomHandler as shown here:

public class CDATAHandler implements DomHandler<String, StreamResult> {

  private static final String START_TAG = "<![CDATA[<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
  private static final String END_TAG = "]]>";

  private StringWriter xmlWriter = new StringWriter();

  public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
     return new StreamResult(xmlWriter);
  }

  public String getElement(StreamResult rt) {
     String xml = rt.getWriter().toString();
     int beginIndex = xml.indexOf(START_TAG) + START_TAG.length();
     int endIndex = xml.indexOf(END_TAG);
     return xml.substring(beginIndex, endIndex);
  }

  public Source marshal(String n, ValidationEventHandler errorHandler) {
     try {
        String xml = START_TAG + n.trim() + END_TAG;
        StringReader xmlReader = new StringReader(xml);
        return new StreamSource(xmlReader);
     } catch(Exception e) {
        throw new RuntimeException(e);
     }
  }
}

在那之后,Jersey 变成了抛出异常(javax.ws.rs.ProcessingException: HTTP 500 Internal Server Error)。这是 StackTrace:

org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:226)
org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:655)
org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:652)
org.glassfish.jersey.internal.Errors.process(Errors.java:315)
org.glassfish.jersey.internal.Errors.process(Errors.java:297)
org.glassfish.jersey.internal.Errors.process(Errors.java:228)
org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:422)
org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:652)
org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:412)
action.BaseExecutor.execute(BaseExecutor.java:42) [my method, where I invoke Jersey]
...

我在处理程序中的所有方法都设置了断点,但没有一个不起作用。

如果我删除 @XmlAnyElement 注释,一切都会好起来的(当然,除了 Jersey 会发送带有“& lt;”和“& gt;”转义的错误 XML)。

--------- 更新 (15.01.14) ----------

我调试了 Jersey 并找到了 root 异常:

javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an @XmlRootElement annotation]

【问题讨论】:

  • &lt;element2&gt;的角度来看,内容只是一个字符串,恰好是一个XML文档。如果您确实必须将其格式化为 CDATA 部分,question 有很多可能的解决方案。
  • JAXB use String as it is的可能重复
  • @BlaiseDoughan 我试过your solution。但是以这种方式,泽西岛变成了抛出异常(javax.ws.rs.ProcessingException:HTTP 500 Internal Server Error)。如果您建议我可能是什么问题或我应该寻找哪种方式,我将不胜感激。

标签: java xml jaxb jersey marshalling


【解决方案1】:

问题已通过将 Jersey JAXB 默认实现更改为 MOXy 解决。

好的,我想,我可以删除我的问题,因为我对我的问题进行了 duirind 调查的所有问题都已经在其他几个问题上得到了回答。

【讨论】:

    猜你喜欢
    • 2011-06-07
    • 2012-03-03
    • 1970-01-01
    • 2017-11-24
    • 2022-11-03
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    相关资源
    最近更新 更多