重命名MOXy JSON编组的元素名称

时间:2014-08-06 09:07:54

标签: java json jaxb jersey moxy

我为JAX-WS(Metro)和JAX-RS(Jersey)使用通用的JAXB模型。我有以下请求代码段:

<xs:element name="CreatedProjFolders">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="CreatedProjFolder" type="tns:CreatedProjFolder" minOccurs="0"
                maxOccurs="unbounded" />
        </xs:sequence>
        <xs:attribute name="parentItemId" type="tns:itemId" use="required" />
    </xs:complexType>
</xs:element>

<xs:complexType name="CreateProjFolder">
    <xs:attribute name="itemId" type="tns:itemId" use="required" />
    ...
</xs:complexType>

XJC生成了这个类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "createProjFolders"
})
@XmlRootElement(name = "CreateProjFolders")
public class CreateProjFolders {

    @XmlElement(name = "CreateProjFolder", required = true)
    @NotNull
    @Size(min = 1)
    @Valid
    // Name has been pluralized with JAXB bindings file
    protected List<CreateProjFolder> createProjFolders;
    @XmlAttribute(name = "parentItemId", required = true)
    @NotNull
    @Size(max = 128)
    protected String parentItemId;

    ...
}

适当的JSON POST应如下所示:

{"parentItemId":"P5J00142301", "createProjFolders":[
  {"itemId":"bogus"}
]}

但实际上必须看起来像:

{"parentItemId":"P5J00142301", "CreateProjFolder":[
  {"itemId":"bogus"}
]}

如何重命名JSON的属性名称,只类似于Java中的属性名称(protected List<CreateProjFolder> createProjFolders)?

2 个答案:

答案 0 :(得分:2)

当MOXy用作JSON绑定提供程序时,JSON键与@XmlElement注释中的specfieid相同。例如,当你有:

@XmlElement(name = "CreateProjFolder", required = true)
protected List<CreateProjFolder> createProjFolders;

你会得到:

{"parentItemId":"P5J00142301", "CreateProjFolder":[
  {"itemId":"bogus"}
]}

如果您希望JSON中的名称与XML中的名称不同,则可以利用MOXy的外部映射元数据来覆盖注释中指定的内容:

答案 1 :(得分:1)

在阅读了Blaise的帖子和博客之后,我花了两天的时间才想出一个有效的解决方案。首先,MOXyJsonProviderConfigurableMoxyJsonProvider的当前状态使其成为一种痛苦的经验,使其成功并且从未为此设计过。

我的第一个测试是创建一个与泽西岛完全分离的洁净室实现,并以main方法运行。

以下是main方法:

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

Map<String, Object> props = new HashMap<>();

InputStream importMoxyBinding = MOXyTest.class
        .getResourceAsStream("/json-binding.xml");

List<InputStream> moxyBindings = new ArrayList<>();
moxyBindings.add(importMoxyBinding);

props.put(JAXBContextProperties.OXM_METADATA_SOURCE, moxyBindings);
props.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
props.put(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);

JAXBContext jc = JAXBContext.newInstance("my.package",
    CreateProjFolders.class.getClassLoader(), props);

Unmarshaller um = jc.createUnmarshaller();

InputStream json = MOXyTest.class
    .getResourceAsStream("/CreateProjFolders.json");
Source source = new StreamSource(json);

JAXBElement<CreateProjFolders> create = um.unmarshal(source, CreateProjFolders.class);
CreateProjFolders folders = create.getValue();

System.out.printf("Used JAXBContext: %s%n", jc);
System.out.printf("Unmarshalled structure: %s%n", folders);

Marshaller m = jc.createMarshaller();
m.setProperty(MarshallerProperties.INDENT_STRING, "    ");
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
System.out.print("Marshalled structure: ");
m.marshal(folders, System.out);

}

此处为json-binding.xml

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="my.package"
    xml-mapping-metadata-complete="false">
    <xml-schema namespace="urn:namespace"
        element-form-default="QUALIFIED" />

    <java-types>
        <java-type name="CreateProjFolders">
            <xml-root-element />
            <java-attributes>
                <xml-element java-attribute="projFolders" name="createProjFolders" />
            </java-attributes>
        </java-type>
        <java-type name="CreateProjFolder">
            <java-attributes>
                <xml-element java-attribute="access" name="access" />
            </java-attributes>
        </java-type>
        <java-type name="Access">
            <java-attributes>
                <xml-element java-attribute="productionSites" name="productionSites" />
            </java-attributes>
        </java-type>
    </java-types>

</xml-bindings>

和示例输入文件:

{"parentItemId":"some-id",
    "createProjFolders":[
    {"objectNameEn":"bogus", "externalProjectId":"123456",
        "access":{"productionSites":["StackOverflow"], "user":"michael-o"}}
    ]
}

解组和编组工作完美无瑕。现在,如何让它与泽西岛合作?你不能,因为你无法传递JAXBContext属性。

由于MOXyJsonProvider功能,您需要将MOXy的AutoDiscoverable以及Jersey Media MOXy的整个源(XML内容)复制到新的Maven项目中。这个包将取代原来的依赖。

应用following patches。修补程序并不完美,因为某些代码是重复的,因此是多余的,但可以在以后的故障单中完成。

现在在Application.class

中确认这一点
InputStream importMoxyBinding = MyApplication.class
    .getResourceAsStream("/json-binding.xml");

List<InputStream> moxyBindings = new ArrayList<>();
moxyBindings.add(importMoxyBinding);

final MoxyJsonConfig jsonConfig = new MoxyJsonConfig();
jsonConfig.setOxmMedatadataSource(moxyBindings);
ContextResolver<MoxyJsonConfig> jsonConfigResolver = jsonConfig.resolver();
register(jsonConfigResolver);

现在试试吧。在针对不同型号的多次通话后,您会看到JAXBExceptions带有&#39;文件过早结束&#39;。你会问你为什么?!原因是MOXyJsonProvider每个域类创建并缓存JAXBContexts而不是每个包,这意味着您的输入流被多次读取但在第一次读取后已经关闭。你迷路了。您需要重置流但不能更改MOXy的内部内容。这是一个简单的solution

public class ResetOnCloseInputStream extends BufferedInputStream {

    public ResetOnCloseInputStream(InputStream is) {
        super(is);
        super.mark(Integer.MAX_VALUE);
    }

    @Override
    public void close() throws IOException {
        super.reset();
    }

}

并将您的Application.class换成

moxyBindings.add(new ResetOnCloseInputStream(importMoxyBinding));

在你感受到屁股的痛苦之后,享受魔力!

最后的话:

  • 我不接受Blaise的回答(给出了upvote),因为它只是解决方案的一小部分,但却引导我走向正确的方向。
  • 这两个课程都很难解决一个非常简单的问题,更令人惊讶的是,我是唯一一个想要通过OXM_METADATA_SOURCE的人。严重?