反序列化 XML 列表使用 Jackson。

huangapple go评论100阅读模式
英文:

deserializing XML list with jackson

问题

Sure, here's the translated code part:

我有一些如下的XML:

    <model>
      <action>
         <items>
            <input name="i1"/>
            <input name="i2"/>
            <output name="o1"/>
            <output name="o2"/>
         </items>
      </action>
    </model>

我正在使用Jackson XMLMapper进行反序列化:

    Model m = xmlMapper.readValue(file, Model.class);

我的Model类包含一个Action属性(带有`@JsonProperty("action")`注释)

我的Action类包含一个`List<Input>`和一个`List<Output>`

在反序列化后,这两个列表都是空的。

我无法弄清楚应该使用什么注释来指示items标签包含输入和输出。

我已经尝试在两个列表上使用`@JacksonXmlElementWrapper`注释,但似乎不起作用。
英文:

I have some xml that looks as follows:

<model>
  <action>
     <items>
        <input name="i1"/>
        <input name="i2"/>
        <output name="o1"/>
        <output name="o2"/>
     </items>
  </action>
</model>

I'm using the Jackson XMLMapper to deserialize it:

Model m = xmlMapper.readValue(file, Model.class);

My Model class contains an Action property (with the @JsonProperty("action") annotation)

My Action class that contains a List<Input> and a List<Output>

Both the lists are empty after deserialization.

I can't figure out what annotations I should use to indicate that the items tag contains both inputs and outputs.

I have tried the @JacksonXmlElementWrapper annotation on both lists, but that doesn't seem to work.

答案1

得分: 1

以下是您要翻译的代码部分:

Jackson does automatically deserialize xml elements with same name into an array but it will fail if multiple arrays are in the same level. You are going to have to use custom deserializer.
here is an example custom deserializer that can successfully deserialize example XML doc

public class ActionDeserializer extends StdDeserializer<Action> {

	public ActionDeserializer() {
		super((Class<?>)null);
	}

	public ActionDeserializer(Class<?> vc) {
		super(vc);
	}

	@Override
	public Action deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {

		Action action = new Action();
		action.inputs = new ArrayList<>();
		action.outputs = new ArrayList<>();

	// <action>
	JsonNode actionNode = parser.getCodec().readTree(parser);
	// <items>
	JsonNode itemsNode = actionNode.get("items");

	// deserialize <input> elements into pojo list
	JsonNode inputNode = itemsNode.get("input");
	if (inputNode.getNodeType() == JsonNodeType.ARRAY) {
		Iterator<JsonNode> iterator = inputNode.iterator();
		while (iterator.hasNext()) {
			action.inputs.add(new XmlMapper().treeToValue(iterator.next(), Input.class));
		}
	}

	// deserialize <output> elements into pojo list
	JsonNode outputNode = itemsNode.get("output");
	if (outputNode.getNodeType() == JsonNodeType.ARRAY) {
		Iterator<JsonNode> iterator = outputNode.iterator();
		while (iterator.hasNext()) {
			action.outputs.add(new XmlMapper().treeToValue(iterator.next(), Output.class));
		}
	}

	return action;
	}
}

you register the custom deserializer using simple module

SimpleModule simpleModule = a SimpleModule();
simpleModule.addDeserializer(Action.class, new ActionDeserializer());
xmlMapper.registerModule(simpleModule);

EDIT:

以下是您提供的完整测试类的代码:

public class Model {

	public Action action;

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

		String xml = """
			<model>
			  <action>
			     <items>
			        <input name="i1"/>
			        <input name="i2"/>
			        <output name="o1"/>
			        <output name="o2"/>
			     </items>
			  </action>
			</model>
			""";

		XmlMapper xmlMapper = new XmlMapper();
		SimpleModule simpleModule = new SimpleModule();
		simpleModule.addDeserializer(Action.class, new ActionDeserializer());
		xmlMapper.registerModule(simpleModule);

		Model m = xmlMapper.readValue(xml, Model.class);
		System.out.println("inputs " + m.action.inputs.stream().map(x -> x.name).toList());
		System.out.println("outputs " + m.action.outputs.stream().map(x -> x.name).toList());
	}

	public static class Action {
		public List<Input> inputs;
		public List<Output> outputs;
	}

	public static class Input {
		public String name;
	}

	public static class Output {
		public String name;
	}

	public static class ActionDeserializer extends StdDeserializer<Action> {

		public ActionDeserializer() {
			super((Class<?>)null);
		}

		@Override
		public Action deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {

			Action action = new Action();
			action.inputs = new ArrayList<>();
			action.outputs = new ArrayList<>();

		// <action>
	JsonNode actionNode = parser.getCodec().readTree(parser);
	// <items>
	JsonNode itemsNode = actionNode.get("items");

	// deserialize <input> elements into pojo list
	JsonNode inputNode = itemsNode.get("input");
	Iterator<JsonNode> iterator = inputNode.iterator();
	while (iterator.hasNext()) {
		action.inputs.add(new XmlMapper().treeToValue(iterator.next(), Input.class));
	}

	// deserialize <output> elements into pojo list
	JsonNode outputNode = itemsNode.get("output");
	iterator = outputNode.iterator();
	while (iterator.hasNext()) {
		action.outputs.add(new XmlMapper().treeToValue(iterator.next(), Output.class));
	}

	return action;
	}
}
}

输出:

inputs [i1, i2]
outputs [o1, o2]
英文:

Jackson does automatically deserialize xml elements with same name into an array but it will fail if multiple arrays are in the same level. You are going to have to use custom deserializer.
here is an example custom deserializer that can successfully deserialize example XML doc

public class ActionDeserializer extends StdDeserializer<Action> {
public ActionDeserializer() {
super((Class<?>)null);
}
public ActionDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Action deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
Action action = new Action();
action.inputs = new ArrayList<>();
action.outputs = new ArrayList<>();
// <action>
JsonNode actionNode = parser.getCodec().readTree(parser);
// <items>
JsonNode itemsNode = actionNode.get("items");
// deserialize <input> elements into pojo list
JsonNode inputNode = itemsNode.get("input");
if (inputNode.getNodeType() == JsonNodeType.ARRAY) {
Iterator<JsonNode> iterator = inputNode.iterator();
while (iterator.hasNext()) {
action.inputs.add(new XmlMapper().treeToValue(iterator.next(), Input.class));
}
}
// deserialize <output> elements into pojo list
JsonNode outputNode = itemsNode.get("output");
if (outputNode.getNodeType() == JsonNodeType.ARRAY) {
Iterator<JsonNode> iterator = outputNode.iterator();
while (iterator.hasNext()) {
action.outputs.add(new XmlMapper().treeToValue(iterator.next(), Output.class));
}
}
return action;
}
}

you register the custom deserializer using simple module

	SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Action.class, new ActionDeserializer());
xmlMapper.registerModule(simpleModule);

EDIT:

here is my complete test class. the custom deserializer works even without the condition on type of json node

public class Model {
public Action action;
public static void main(String[] args) throws JsonProcessingException {
String xml = """
<model>
<action>
<items>
<input name="i1"/>
<input name="i2"/>
<output name="o1"/>
<output name="o2"/>
</items>
</action>
</model>
""";
XmlMapper xmlMapper = new XmlMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Action.class, new ActionDeserializer());
xmlMapper.registerModule(simpleModule);
Model m = xmlMapper.readValue(xml, Model.class);
System.out.println("inputs " + m.action.inputs.stream().map(x -> x.name).toList());
System.out.println("outputs " + m.action.outputs.stream().map(x -> x.name).toList());
}
public static class Action {
public List<Input> inputs;
public List<Output> outputs;
}
public static class Input {
public String name;
}
public static class Output {
public String name;
}
public static class ActionDeserializer extends StdDeserializer<Action> {
public ActionDeserializer() {
super((Class<?>)null);
}
@Override
public Action deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
Action action = new Action();
action.inputs = new ArrayList<>();
action.outputs = new ArrayList<>();
// <action>
JsonNode actionNode = parser.getCodec().readTree(parser);
// <items>
JsonNode itemsNode = actionNode.get("items");
// deserialize <input> elements into pojo list
JsonNode inputNode = itemsNode.get("input");
Iterator<JsonNode> iterator = inputNode.iterator();
while (iterator.hasNext()) {
action.inputs.add(new XmlMapper().treeToValue(iterator.next(), Input.class));
}
// deserialize <output> elements into pojo list
JsonNode outputNode = itemsNode.get("output");
iterator = outputNode.iterator();
while (iterator.hasNext()) {
action.outputs.add(new XmlMapper().treeToValue(iterator.next(), Output.class));
}
return action;
}
}
}

output

inputs [i1, i2]
outputs [o1, o2]

huangapple
  • 本文由 发表于 2023年3月20日 23:54:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/75792527.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定