JAXB/Moxy 解组将所有字段值分配给 Map<String,Object> 而不是为其提供的特定字段
JAXB/Moxy Unmarshalling assigns all field values to Map<String,Object> rather than the specific field provided for it
简而言之,我想执行 unmarshalling
as mentioned here 但是除了 Map
我还会有一个 @XmlElement
。所以一个字段用 (Map field) @XmlPath(".")
注释,另一个字段用 (String field) @XmlElement
注释,然后我想执行 unmarshalling
.
我的应用程序的主要目标是使用 JAXB/Moxy and Jackson
库转换 XML->JSON
和 JSON->XML
。我正在尝试 unmarshal
XML 并将其映射到 Java POJO。我的 XML 可以有一些专用元素和一些随机出现的 user-defined 元素,所以我想将它们存储在 Map<String, Object>
中。因此,我正在使用 XMLAdapter
。我正在关注 blog article to do so。我做的不是完全一样,而是有点不同。
我面临的问题是在 unmarshalling
期间根本没有考虑专用字段。所有的值都是 unmarshalled
到 Map<String.Object>
。据我了解,这是由于注释 @XmlPath(".")
和 XMLAdapter
的使用而发生的,但是如果我删除此注释,它将无法按预期工作。有人可以帮我解决这个问题吗? marshaling
适用于 @XmlPath(".")
和 XMLAdapter
。该问题仅在 unmarshalling
.
期间出现
以下是我要转换为 JSON
的 XML
:(注意:Name
和 Age
是专用字段,others
是user-defined
字段。)
<Customer xmlns:google="https://google.com">
<name>BATMAN</name>
<age>2008</age>
<google:main>
<google:sub>bye</google:sub>
</google:main>
</Customer>
以下是我的 Customer
class 被 Moxy
和 Jackson
用于 marshaling
, unmarshalling
: (注意: Name
和 Age
是专用字段, others
是 user-defined
字段。我希望 others
仅存储无法直接映射到 POJO 的值,例如 google:main
及其上面的 children XML)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "others"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String name;
private String age;
@XmlPath(".")
@XmlJavaTypeAdapter(TestAdapter.class)
private Map<String, Object> others;
//Getter, Setter and other constructors
}
以下是我的 TestAdapter
class,它将用于 Userdefined
字段:
class TestAdapter extends XmlAdapter<Wrapper, Map<String, Object>> {
@Override
public Map<String, Object> unmarshal(Wrapper value) throws Exception {
System.out.println("INSIDE UNMARSHALLING METHOD TEST");
final Map<String, Object> others = new HashMap<>();
for (Object obj : value.getElements()) {
final Element element = (Element) obj;
final NodeList children = element.getChildNodes();
//Check if its direct String value field or complex
if (children.getLength() == 1) {
others.put(element.getNodeName(), element.getTextContent());
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Wrapper wrapper = new Wrapper();
List childElements = new ArrayList();
childElements.add(n);
wrapper.elements = childElements;
child.add(unmarshal(wrapper));
}
}
others.put(element.getNodeName(), child);
}
}
return others;
}
@Override
public Wrapper marshal(Map<String, Object> v) throws Exception {
Wrapper wrapper = new Wrapper();
List elements = new ArrayList();
for (Map.Entry<String, Object> property : v.entrySet()) {
if (property.getValue() instanceof Map) {
elements.add(new JAXBElement<Wrapper>(new QName(property.getKey()), Wrapper.class, marshal((Map) property.getValue())));
} else {
elements.add(new JAXBElement<String>(new QName(property.getKey()), String.class, property.getValue().toString()));
}
}
wrapper.elements = elements;
return wrapper;
}
}
@Getter
class Wrapper {
@XmlAnyElement
List elements;
}
最后,我的 Main
class 将用于 marshaling
和 unmarshalling
。此外,转换为 JSON 和 XML.
class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException, JsonProcessingException {
//XML to JSON
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("Customer.xml");
final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
final XMLStreamReader streamReader = xmlInputFactory.createXMLStreamReader(inputStream);
final Customer customer = unmarshaller.unmarshal(streamReader, Customer.class).getValue();
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonEvent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(jsonEvent);
//JSON to XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer, System.out);
}
}
当我转换 XML->JSON
时,我得到以下输出:(如果您观察到 name
和 age
字段未被视为 [=46= 中的专用字段] class 而不是将其作为随机字段写入 others
)
{
"name" : "",
"age" : "",
"others" : {
"google:main" : [ {
"google:sub" : "bye"
} ],
"name" : "BATMAN",
"age" : "2008"
}
}
我希望我的输出是这样的:(我希望首先映射我的专用字段,然后如果有任何未知字段则稍后在 others MAP
中映射它们)。请注意,我不想在我的 JSON
中获得 others
标签。我只想获取专用字段的字段名称。
{
"name": "BATMAN",
"age": 2008,
"google:main": {
"google:sub": "bye"
}
}
以下是我想在 marshaling
期间获得的 XML
。另外,请注意我正在使用 @XmlPath(".")
,这样我在 marshaling
.
期间不会在 XML
中获得 others
节点
<Customer>
<name>BATMAN</name>
<age>2008</age>
<google:main>>
<google:sub>bye</google:sub>
</google:main>
</Customer>
marshaling
工作正常。问题发生在 .unmarshaling
期间,据我所知,这是由于带有 XMLAdapter
的注释 @XmlPath(".")
而发生的,但是如果我删除此注释,它将无法按预期工作。有人可以帮我解决这个问题吗?
** 已编辑 **
我想到了一些解决方法,但似乎对我没有任何作用。由于 @XmlPath(".")
,他们变得一团糟。仍在寻找一些想法或解决方法。任何帮助将不胜感激。
我认为这与以下问题有些相关:
根据漏洞单:
org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl
public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) {
...
Line 1279
if(null == resultNode && null == nonPredicateNode) {
// ANY MAPPING
resultNode = xPathNode.getAnyNode();// by default it return the EventAdapter returing a null at this place fix the problem, but i dont know if its the best solution
}
作为(仅)解决方法,专用字段可以转到单独的 class,因此它可以自行解组。 “客户”将从这个 class.
扩展
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Person", propOrder = { "name", "age"})
public class Person {
protected String name;
protected String age;
//getters and stuff
}
客户
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer") //, propOrder = { "name", "age", "others" }
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer extends Person{
@XmlPath(".")
@XmlJavaTypeAdapter(TestAdapter.class)
private Map<String, Object> others;
}
自定义适配器无法解析没有命名空间的元素或排除添加到 Person class 的特定命名空间。
@Override
public Map<String, Object> unmarshal(Wrapper value) throws Exception {
System.out.println("INSIDE UNMARSHALLING METHOD TEST");
final Map<String, Object> others = new HashMap<>();
for (Object obj : value.getElements()) {
final Element element = (Element) obj;
final NodeList children = element.getChildNodes();
System.out.println(element.getNodeName()+ " children.getLength(): " + children.getLength() + ", ns: " + element.getNamespaceURI());
if (element.getNamespaceURI() != null) {
// Check if its direct String value field or complex
if (children.getLength() == 1) {
others.put(element.getNodeName(), element.getTextContent());
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Wrapper wrapper = new Wrapper();
List<Object> childElements = new ArrayList<Object>();
childElements.add(n);
wrapper.elements = childElements;
child.add(unmarshal(wrapper));
}
}
others.put(element.getNodeName(), child);
}
}
}
return others;
}
最后,解析将使用 DOM 对象,因此输入被读取一次
public static void main(String[] args) throws Exception {
// XML to JSON
jakarta.xml.bind.JAXBContext jaxbContext = JAXBContext.newInstance(Person.class, Customer.class);
jakarta.xml.bind.Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
File initialFile = new File("src/main/resources/Customer.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(initialFile);
final Person person = unmarshaller.unmarshal(doc, Person.class).getValue();
final Customer customer = unmarshaller.unmarshal(doc, Customer.class).getValue();
System.out.println("unmarshall:" + customer);
customer.setName(person.getName());
customer.setAge(person.getAge());
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonEvent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(jsonEvent);
// JSON to XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer, System.out);
}
啊,总算松了一口气。这个问题让我很头疼,但我终于找到了解决方法。尝试了很多东西并联系了很多人,但似乎没有任何效果,我认为这是 JAXB/Moxy
库的问题。我找到了解决方法。希望它对以后的人有帮助,不要像我一样沮丧:)
我使用了 2 个字段,一个带有 @XmlAnyElement(lax=true) List<Object>
用于在 marshaling
期间存储元素,另一个 Map<String, Object>
用于 JSON
的自定义序列化。除此之外,我了解到我们可以使用 beforeMarshal
、afterMarshal
、beforeUnmarshal
、afterMarshal
方法。这个名字本身就暗示了它的作用。
在我的例子中,我使用 beforeMarshal
方法将来自 Map<String, Object>
的未知数据添加到 List<Object>
,因此在 List<Object>
的 marshaling
值期间] 将会被使用。我删除了 XMLAdapter
.
此外,afterUnmarshal
方法将从 List<Object>
读取的未知元素添加到 Map<String, Object>
,因此 Jackson
可以利用它并写入 JSON
使用CustomSearlizer
.
基本上,这是一种捉迷藏的方法。 List<Object>
将在 unmarshalling
和 marshaling
期间被 JAXB/Moxy
使用。 Map<String, Object>
将在 Jackson
的 serialization and deserialization
期间使用。
Custome.class
与我的 beforeMarshal
和 afterUnmarshalling
:安排。你可以根据需要进行更改)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "otherElements"})
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@AllArgsConstructor
@ToString
public class Customer {
@XmlTransient
private String isA;
private String name;
private String age;
@XmlAnyElement(lax = true)
@JsonIgnore
private List<Object> otherElements = new ArrayList<>();
@JsonIgnore
@XmlTransient
private Map<String, Object> userExtensions = new HashMap<>();
@JsonAnyGetter
@JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
userExtensions.put(key, value);
}
private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
System.out.println("Before Marshalling User Extension: " + userExtensions);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
otherElements = extensionsModifier.Marshalling(userExtensions);
System.out.println("Before Marshalling Final Other Elements " + otherElements);
userExtensions = new HashMap<>();
}
private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
System.out.println("After Unmarshalling : " + otherElements);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
userExtensions = extensionsModifier.Unmarshalling(otherElements);
otherElements = new ArrayList();
}
}
然后 ExtensionsModifier.class
将被 beforeMarshal
和 afterUnmarshalling
方法调用:
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ExtensionsModifier {
private javax.xml.parsers.DocumentBuilderFactory documentFactory;
private javax.xml.parsers.DocumentBuilder documentBuilder;
private org.w3c.dom.Document document;
public ExtensionsModifier() throws ParserConfigurationException {
documentFactory = DocumentBuilderFactory.newInstance();
documentBuilder = documentFactory.newDocumentBuilder();
document = documentBuilder.newDocument();
}
public List<Object> Marshalling(Map<String, Object> userExtensions) throws ParserConfigurationException {
if (userExtensions == null) {
return null;
}
List<Object> tempElement = new ArrayList<>();
for (Map.Entry<String, Object> property : userExtensions.entrySet()) {
Element root = document.createElement(property.getKey());
if (property.getValue() instanceof Map) {
List<Object> mapElements = Marshalling((Map<String, Object>) property.getValue());
mapElements.forEach(innerChildren -> {
if (innerChildren instanceof Element) {
if (((Element) innerChildren).getTextContent() != null) {
root.appendChild(document.appendChild((Element) innerChildren));
}
}
});
tempElement.add(root);
} else if (property.getValue() instanceof String) {
root.setTextContent(((String) property.getValue()));
tempElement.add(root);
} else if (property.getValue() instanceof ArrayList) {
for (Object dupItems : (ArrayList<Object>) property.getValue()) {
if (dupItems instanceof Map) {
Element arrayMap = document.createElement(property.getKey());
List<Object> arrayMapElements = Marshalling((Map<String, Object>) dupItems);
arrayMapElements.forEach(mapChildren -> {
if (mapChildren instanceof Element) {
if (((Element) mapChildren).getTextContent() != null) {
arrayMap.appendChild(document.appendChild((Element) mapChildren));
}
}
});
tempElement.add(arrayMap);
} else if (dupItems instanceof String) {
Element arrayString = document.createElement(property.getKey());
arrayString.setTextContent((String) dupItems);
tempElement.add(arrayString);
}
}
}
}
return tempElement;
}
public Map<String, Object> Unmarshalling(List<Object> value) {
if (value == null) {
return null;
}
final Map<String, Object> extensions = new HashMap<>();
for (Object obj : value) {
org.w3c.dom.Element element = (org.w3c.dom.Element) obj;
final NodeList children = element.getChildNodes();
//System.out.println("Node Name : " + element.getNodeName() + " Value : " + element.getTextContent());
List<Object> values = (List<Object>) extensions.get(element.getNodeName());
if (values == null) {
values = new ArrayList<Object>();
}
if (children.getLength() == 1) {
values.add(element.getTextContent());
extensions.put(element.getNodeName(), values);
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
List<Object> childElements = new ArrayList();
childElements.add(n);
values.add(Unmarshalling(childElements));
child.add(Unmarshalling(childElements));
}
}
extensions.put(element.getNodeName(), values);
}
}
return extensions;
}
}
以下是我的 CustomSearlizer
,Jackson
将使用它来创建 JSON:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
public class CustomExtensionsSerializer extends JsonSerializer<Map<String, Object>> {
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
System.out.println("Custom Json Searlizer: " + value);
recusiveSerializer(value, gen, serializers);
}
public void recusiveSerializer(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
for (Map.Entry<String, Object> extension : value.entrySet()) {
if (extension.getValue() instanceof Map) {
//If instance is MAP then call the recursive method
recusiveSerializer((Map) extension.getValue(), gen, serializers);
} else if (extension.getValue() instanceof String) {
//If instance is String directly add it to the JSON
gen.writeStringField(extension.getKey(), (String) extension.getValue());
} else if (extension.getValue() instanceof ArrayList) {
//If instance if ArrayList then loop over it and add it to the JSON after calling recursive method
//If size more than 1 add outer elements
if (((ArrayList<Object>) extension.getValue()).size() > 1) {
gen.writeFieldName(extension.getKey());
gen.writeStartObject();
for (Object dupItems : (ArrayList<Object>) extension.getValue()) {
if (dupItems instanceof Map) {
recusiveSerializer((Map) dupItems, gen, serializers);
} else {
gen.writeStringField(extension.getKey(), (String) dupItems);
}
}
gen.writeEndObject();
} else {
for (Object dupItems : (ArrayList<Object>) extension.getValue()) {
if (dupItems instanceof Map) {
gen.writeFieldName(extension.getKey());
gen.writeStartObject();
recusiveSerializer((Map) dupItems, gen, serializers);
gen.writeEndObject();
} else {
gen.writeStringField(extension.getKey(), (String) dupItems);
}
}
}
}
}
}
}
如果我提供如下输入 XML
:
<Customer xmlns:google="https://google.com">
<name>Rise Against</name>
<age>2000</age>
<google:main>
<google:sub>MyValue</google:sub>
<google:sub>MyValue</google:sub>
</google:main>
</Customer>
然后我得到以下 JSON
作为输出:
{
"isA" : "Customer",
"name" : "Rise Against",
"age" : "2000",
"google:main" : {
"google:sub" : "MyValue",
"google:sub" : "MyValue"
}
}
反之亦然。希望说清楚如果没有留下评论会尽量回复。
简而言之,我想执行 unmarshalling
as mentioned here 但是除了 Map
我还会有一个 @XmlElement
。所以一个字段用 (Map field) @XmlPath(".")
注释,另一个字段用 (String field) @XmlElement
注释,然后我想执行 unmarshalling
.
我的应用程序的主要目标是使用 JAXB/Moxy and Jackson
库转换 XML->JSON
和 JSON->XML
。我正在尝试 unmarshal
XML 并将其映射到 Java POJO。我的 XML 可以有一些专用元素和一些随机出现的 user-defined 元素,所以我想将它们存储在 Map<String, Object>
中。因此,我正在使用 XMLAdapter
。我正在关注 blog article to do so。我做的不是完全一样,而是有点不同。
我面临的问题是在 unmarshalling
期间根本没有考虑专用字段。所有的值都是 unmarshalled
到 Map<String.Object>
。据我了解,这是由于注释 @XmlPath(".")
和 XMLAdapter
的使用而发生的,但是如果我删除此注释,它将无法按预期工作。有人可以帮我解决这个问题吗? marshaling
适用于 @XmlPath(".")
和 XMLAdapter
。该问题仅在 unmarshalling
.
以下是我要转换为 JSON
的 XML
:(注意:Name
和 Age
是专用字段,others
是user-defined
字段。)
<Customer xmlns:google="https://google.com">
<name>BATMAN</name>
<age>2008</age>
<google:main>
<google:sub>bye</google:sub>
</google:main>
</Customer>
以下是我的 Customer
class 被 Moxy
和 Jackson
用于 marshaling
, unmarshalling
: (注意: Name
和 Age
是专用字段, others
是 user-defined
字段。我希望 others
仅存储无法直接映射到 POJO 的值,例如 google:main
及其上面的 children XML)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "others"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String name;
private String age;
@XmlPath(".")
@XmlJavaTypeAdapter(TestAdapter.class)
private Map<String, Object> others;
//Getter, Setter and other constructors
}
以下是我的 TestAdapter
class,它将用于 Userdefined
字段:
class TestAdapter extends XmlAdapter<Wrapper, Map<String, Object>> {
@Override
public Map<String, Object> unmarshal(Wrapper value) throws Exception {
System.out.println("INSIDE UNMARSHALLING METHOD TEST");
final Map<String, Object> others = new HashMap<>();
for (Object obj : value.getElements()) {
final Element element = (Element) obj;
final NodeList children = element.getChildNodes();
//Check if its direct String value field or complex
if (children.getLength() == 1) {
others.put(element.getNodeName(), element.getTextContent());
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Wrapper wrapper = new Wrapper();
List childElements = new ArrayList();
childElements.add(n);
wrapper.elements = childElements;
child.add(unmarshal(wrapper));
}
}
others.put(element.getNodeName(), child);
}
}
return others;
}
@Override
public Wrapper marshal(Map<String, Object> v) throws Exception {
Wrapper wrapper = new Wrapper();
List elements = new ArrayList();
for (Map.Entry<String, Object> property : v.entrySet()) {
if (property.getValue() instanceof Map) {
elements.add(new JAXBElement<Wrapper>(new QName(property.getKey()), Wrapper.class, marshal((Map) property.getValue())));
} else {
elements.add(new JAXBElement<String>(new QName(property.getKey()), String.class, property.getValue().toString()));
}
}
wrapper.elements = elements;
return wrapper;
}
}
@Getter
class Wrapper {
@XmlAnyElement
List elements;
}
最后,我的 Main
class 将用于 marshaling
和 unmarshalling
。此外,转换为 JSON 和 XML.
class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException, JsonProcessingException {
//XML to JSON
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("Customer.xml");
final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
final XMLStreamReader streamReader = xmlInputFactory.createXMLStreamReader(inputStream);
final Customer customer = unmarshaller.unmarshal(streamReader, Customer.class).getValue();
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonEvent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(jsonEvent);
//JSON to XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer, System.out);
}
}
当我转换 XML->JSON
时,我得到以下输出:(如果您观察到 name
和 age
字段未被视为 [=46= 中的专用字段] class 而不是将其作为随机字段写入 others
)
{
"name" : "",
"age" : "",
"others" : {
"google:main" : [ {
"google:sub" : "bye"
} ],
"name" : "BATMAN",
"age" : "2008"
}
}
我希望我的输出是这样的:(我希望首先映射我的专用字段,然后如果有任何未知字段则稍后在 others MAP
中映射它们)。请注意,我不想在我的 JSON
中获得 others
标签。我只想获取专用字段的字段名称。
{
"name": "BATMAN",
"age": 2008,
"google:main": {
"google:sub": "bye"
}
}
以下是我想在 marshaling
期间获得的 XML
。另外,请注意我正在使用 @XmlPath(".")
,这样我在 marshaling
.
XML
中获得 others
节点
<Customer>
<name>BATMAN</name>
<age>2008</age>
<google:main>>
<google:sub>bye</google:sub>
</google:main>
</Customer>
marshaling
工作正常。问题发生在 .unmarshaling
期间,据我所知,这是由于带有 XMLAdapter
的注释 @XmlPath(".")
而发生的,但是如果我删除此注释,它将无法按预期工作。有人可以帮我解决这个问题吗?
** 已编辑 **
我想到了一些解决方法,但似乎对我没有任何作用。由于 @XmlPath(".")
,他们变得一团糟。仍在寻找一些想法或解决方法。任何帮助将不胜感激。
我认为这与以下问题有些相关:
根据漏洞单:
org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl
public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) {
...
Line 1279
if(null == resultNode && null == nonPredicateNode) {
// ANY MAPPING
resultNode = xPathNode.getAnyNode();// by default it return the EventAdapter returing a null at this place fix the problem, but i dont know if its the best solution
}
作为(仅)解决方法,专用字段可以转到单独的 class,因此它可以自行解组。 “客户”将从这个 class.
扩展@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Person", propOrder = { "name", "age"})
public class Person {
protected String name;
protected String age;
//getters and stuff
}
客户
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer") //, propOrder = { "name", "age", "others" }
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer extends Person{
@XmlPath(".")
@XmlJavaTypeAdapter(TestAdapter.class)
private Map<String, Object> others;
}
自定义适配器无法解析没有命名空间的元素或排除添加到 Person class 的特定命名空间。
@Override
public Map<String, Object> unmarshal(Wrapper value) throws Exception {
System.out.println("INSIDE UNMARSHALLING METHOD TEST");
final Map<String, Object> others = new HashMap<>();
for (Object obj : value.getElements()) {
final Element element = (Element) obj;
final NodeList children = element.getChildNodes();
System.out.println(element.getNodeName()+ " children.getLength(): " + children.getLength() + ", ns: " + element.getNamespaceURI());
if (element.getNamespaceURI() != null) {
// Check if its direct String value field or complex
if (children.getLength() == 1) {
others.put(element.getNodeName(), element.getTextContent());
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Wrapper wrapper = new Wrapper();
List<Object> childElements = new ArrayList<Object>();
childElements.add(n);
wrapper.elements = childElements;
child.add(unmarshal(wrapper));
}
}
others.put(element.getNodeName(), child);
}
}
}
return others;
}
最后,解析将使用 DOM 对象,因此输入被读取一次
public static void main(String[] args) throws Exception {
// XML to JSON
jakarta.xml.bind.JAXBContext jaxbContext = JAXBContext.newInstance(Person.class, Customer.class);
jakarta.xml.bind.Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
File initialFile = new File("src/main/resources/Customer.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(initialFile);
final Person person = unmarshaller.unmarshal(doc, Person.class).getValue();
final Customer customer = unmarshaller.unmarshal(doc, Customer.class).getValue();
System.out.println("unmarshall:" + customer);
customer.setName(person.getName());
customer.setAge(person.getAge());
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonEvent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(jsonEvent);
// JSON to XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer, System.out);
}
啊,总算松了一口气。这个问题让我很头疼,但我终于找到了解决方法。尝试了很多东西并联系了很多人,但似乎没有任何效果,我认为这是 JAXB/Moxy
库的问题。我找到了解决方法。希望它对以后的人有帮助,不要像我一样沮丧:)
我使用了 2 个字段,一个带有 @XmlAnyElement(lax=true) List<Object>
用于在 marshaling
期间存储元素,另一个 Map<String, Object>
用于 JSON
的自定义序列化。除此之外,我了解到我们可以使用 beforeMarshal
、afterMarshal
、beforeUnmarshal
、afterMarshal
方法。这个名字本身就暗示了它的作用。
在我的例子中,我使用 beforeMarshal
方法将来自 Map<String, Object>
的未知数据添加到 List<Object>
,因此在 List<Object>
的 marshaling
值期间] 将会被使用。我删除了 XMLAdapter
.
此外,afterUnmarshal
方法将从 List<Object>
读取的未知元素添加到 Map<String, Object>
,因此 Jackson
可以利用它并写入 JSON
使用CustomSearlizer
.
基本上,这是一种捉迷藏的方法。 List<Object>
将在 unmarshalling
和 marshaling
期间被 JAXB/Moxy
使用。 Map<String, Object>
将在 Jackson
的 serialization and deserialization
期间使用。
Custome.class
与我的 beforeMarshal
和 afterUnmarshalling
:安排。你可以根据需要进行更改)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "otherElements"})
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@AllArgsConstructor
@ToString
public class Customer {
@XmlTransient
private String isA;
private String name;
private String age;
@XmlAnyElement(lax = true)
@JsonIgnore
private List<Object> otherElements = new ArrayList<>();
@JsonIgnore
@XmlTransient
private Map<String, Object> userExtensions = new HashMap<>();
@JsonAnyGetter
@JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
userExtensions.put(key, value);
}
private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
System.out.println("Before Marshalling User Extension: " + userExtensions);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
otherElements = extensionsModifier.Marshalling(userExtensions);
System.out.println("Before Marshalling Final Other Elements " + otherElements);
userExtensions = new HashMap<>();
}
private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
System.out.println("After Unmarshalling : " + otherElements);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
userExtensions = extensionsModifier.Unmarshalling(otherElements);
otherElements = new ArrayList();
}
}
然后 ExtensionsModifier.class
将被 beforeMarshal
和 afterUnmarshalling
方法调用:
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ExtensionsModifier {
private javax.xml.parsers.DocumentBuilderFactory documentFactory;
private javax.xml.parsers.DocumentBuilder documentBuilder;
private org.w3c.dom.Document document;
public ExtensionsModifier() throws ParserConfigurationException {
documentFactory = DocumentBuilderFactory.newInstance();
documentBuilder = documentFactory.newDocumentBuilder();
document = documentBuilder.newDocument();
}
public List<Object> Marshalling(Map<String, Object> userExtensions) throws ParserConfigurationException {
if (userExtensions == null) {
return null;
}
List<Object> tempElement = new ArrayList<>();
for (Map.Entry<String, Object> property : userExtensions.entrySet()) {
Element root = document.createElement(property.getKey());
if (property.getValue() instanceof Map) {
List<Object> mapElements = Marshalling((Map<String, Object>) property.getValue());
mapElements.forEach(innerChildren -> {
if (innerChildren instanceof Element) {
if (((Element) innerChildren).getTextContent() != null) {
root.appendChild(document.appendChild((Element) innerChildren));
}
}
});
tempElement.add(root);
} else if (property.getValue() instanceof String) {
root.setTextContent(((String) property.getValue()));
tempElement.add(root);
} else if (property.getValue() instanceof ArrayList) {
for (Object dupItems : (ArrayList<Object>) property.getValue()) {
if (dupItems instanceof Map) {
Element arrayMap = document.createElement(property.getKey());
List<Object> arrayMapElements = Marshalling((Map<String, Object>) dupItems);
arrayMapElements.forEach(mapChildren -> {
if (mapChildren instanceof Element) {
if (((Element) mapChildren).getTextContent() != null) {
arrayMap.appendChild(document.appendChild((Element) mapChildren));
}
}
});
tempElement.add(arrayMap);
} else if (dupItems instanceof String) {
Element arrayString = document.createElement(property.getKey());
arrayString.setTextContent((String) dupItems);
tempElement.add(arrayString);
}
}
}
}
return tempElement;
}
public Map<String, Object> Unmarshalling(List<Object> value) {
if (value == null) {
return null;
}
final Map<String, Object> extensions = new HashMap<>();
for (Object obj : value) {
org.w3c.dom.Element element = (org.w3c.dom.Element) obj;
final NodeList children = element.getChildNodes();
//System.out.println("Node Name : " + element.getNodeName() + " Value : " + element.getTextContent());
List<Object> values = (List<Object>) extensions.get(element.getNodeName());
if (values == null) {
values = new ArrayList<Object>();
}
if (children.getLength() == 1) {
values.add(element.getTextContent());
extensions.put(element.getNodeName(), values);
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
List<Object> childElements = new ArrayList();
childElements.add(n);
values.add(Unmarshalling(childElements));
child.add(Unmarshalling(childElements));
}
}
extensions.put(element.getNodeName(), values);
}
}
return extensions;
}
}
以下是我的 CustomSearlizer
,Jackson
将使用它来创建 JSON:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
public class CustomExtensionsSerializer extends JsonSerializer<Map<String, Object>> {
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
System.out.println("Custom Json Searlizer: " + value);
recusiveSerializer(value, gen, serializers);
}
public void recusiveSerializer(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
for (Map.Entry<String, Object> extension : value.entrySet()) {
if (extension.getValue() instanceof Map) {
//If instance is MAP then call the recursive method
recusiveSerializer((Map) extension.getValue(), gen, serializers);
} else if (extension.getValue() instanceof String) {
//If instance is String directly add it to the JSON
gen.writeStringField(extension.getKey(), (String) extension.getValue());
} else if (extension.getValue() instanceof ArrayList) {
//If instance if ArrayList then loop over it and add it to the JSON after calling recursive method
//If size more than 1 add outer elements
if (((ArrayList<Object>) extension.getValue()).size() > 1) {
gen.writeFieldName(extension.getKey());
gen.writeStartObject();
for (Object dupItems : (ArrayList<Object>) extension.getValue()) {
if (dupItems instanceof Map) {
recusiveSerializer((Map) dupItems, gen, serializers);
} else {
gen.writeStringField(extension.getKey(), (String) dupItems);
}
}
gen.writeEndObject();
} else {
for (Object dupItems : (ArrayList<Object>) extension.getValue()) {
if (dupItems instanceof Map) {
gen.writeFieldName(extension.getKey());
gen.writeStartObject();
recusiveSerializer((Map) dupItems, gen, serializers);
gen.writeEndObject();
} else {
gen.writeStringField(extension.getKey(), (String) dupItems);
}
}
}
}
}
}
}
如果我提供如下输入 XML
:
<Customer xmlns:google="https://google.com">
<name>Rise Against</name>
<age>2000</age>
<google:main>
<google:sub>MyValue</google:sub>
<google:sub>MyValue</google:sub>
</google:main>
</Customer>
然后我得到以下 JSON
作为输出:
{
"isA" : "Customer",
"name" : "Rise Against",
"age" : "2000",
"google:main" : {
"google:sub" : "MyValue",
"google:sub" : "MyValue"
}
}
反之亦然。希望说清楚如果没有留下评论会尽量回复。