Переименовать имена элементов для маршаллинга MOXy JSON

1

Я использую общую модель JAXB для JAX-WS (Metro) и JAX-RS (Джерси). У меня есть следующий фрагмент запроса:

<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)?

  • 0
    если вы можете изменить xsd .. <xs: element name = "madeProjFolder" type = "tns: CreatedProjFolder" minOccurs = "0" maxOccurs = "unbounded" />
  • 0
    @ Xstian, не совсем. Мне нужно сохранить имена элементов в паскале и атрибуты в случае верблюда для согласованности. Даже если это не приведет к плюрализации имени свойства в JSON.
Теги:
jaxb
jersey
moxy

2 ответа

1
Лучший ответ

После чтения сообщения Блейза и блога мне потребовалось два дня, чтобы придумать рабочее решение. Прежде всего, текущее состояние MOXyJsonProvider и ConfigurableMoxyJsonProvider делает его непринужденным, чтобы он работал и никогда не был разработан для этого.

Мой первый тест состоял в том, чтобы сделать чистую комнату, которая полностью отделена от Джерси и работает 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.

Вам нужно скопировать MOXy MOXyJsonProvider и весь источник Jersey Media MOXy, за исключением XML-материала в новый проект Maven из-за функции AutoDiscoverable. Этот пакет заменит исходную зависимость.

Примените следующие патчи. Патчи не идеальны и могут быть импровизированы, потому что некоторый код дублируется, поэтому избыточен, но это может быть сделано в билет позже.

Теперь подтвердите, что в вашем 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 с "преждевременным концом файла". Вы спросите, почему?! Причина в том, что MOXyJsonProvider создает и кэширует JAXBContexts каждого класса домена, а не для каждого пакета, что означает, что ваш поток ввода читается несколько раз, но уже был закрыт после первого чтения. Ты потерялся. Вам нужно сбросить поток, но не можете изменить внутренние кишки MOXy. Вот простое решение для этого:

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));

После того, как вы почувствовали боль в заднице, наслаждайтесь волшебством!

Заключительные слова:

  • Я не согласился с ответом Блейза (выше), потому что это была лишь часть решения, но привело меня в правильное направление.
  • Оба класса затрудняют решение очень простой проблемы, более удивительно, что я, OXM_METADATA_SOURCE единственный, кто хочет передать OXM_METADATA_SOURCE. Шутки в сторону?
2

Когда MOXy используется как ваш поставщик JSON-привязки, ключи JSON будут такими же, как и specfieid в аннотациях @XmlElement. Например, если у вас есть:

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

Ты получишь:

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

Если вам нужны разные имена в JSON, чем в XML, вы можете использовать MOXy для внешних метаданных сопоставления, чтобы переопределить то, что было указано в аннотациях:

  • 0
    Спасибо, Блез, я постараюсь доложить!
  • 0
    Я использую это с Джерси, так как я могу предоставить соответствующий OXM_KEY для этого маршалинга и не касаться маршаллинга XML?
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню