将JSON响应映射到不同类型

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

Mapping JSON response to different types

问题

我正在使用Spring 2.6并通过以下方式进行GET请求

`restTemplate.exchange(url, HttpMethod.GET, httpEntity, ResponseType.class).getBody();`

JSON响应可以是两种类型

第一种

```java
public class ResponseType {
    private String data;
}

第二种:

public class ResponseType {
    private Subclass data;
}

public class Subclass {
    private String classId;
    private String detail;
}

在第一种情况下,我只得到对子类资源的引用链接。如果URL包含一个 'resolve' 标记,那么引用链接将在第一个请求中就被展开了。classId 也指定了它是什么样的类('a.b.c' 或 'x.y.z')

对于JSON来说没问题,但在Java中如何进行映射呢?

当有更多字段是动态的(链接或基于classId的实例),如果组合可以是2个链接和3个对象,手动实现可能会很困难。也可能一个对象具有相同的特性 - 一个带有链接的字段或由classId指定的类的实例。

JSON响应可能是这样的:

{  
    "data": "abskasdkjhkjsahfkajdf-linkToResource" 
}

或者是这样的:

{  
    "data": {
        "classId": "a.b.subclass",
        "detail": "some data"   
    } 
}

或者是这样的:

{  
    "data": {
        "classId": "a.b.subclass",
        "detail": "some data",
        "data2": "some-link-id",
        "data3": {
            "detailB": "foo",
            "detailC": "some-link-id"
        } 
    } 
}

<details>
<summary>英文:</summary>

I&#39;m using Spring 2.6 and we make a GET request via

`restTemplate.exchange(url, HttpMethod.GET, httpEntity, ResponseType.class).getBody();`

The JSON response can be of two kinds:

1st:

public class ResponseType {

private String data;

}


2nd:

public class ResponseType {

private Subclass data;

}

public class Subclass {

private String classId;
private String detail;

}


In the first version I only get a reference link to the subclass resource.
If the URL contains a &#39;resolve&#39; flag, than the reference link get expanded already in the first request.
The classId then also specifies what kind of class it is ( &#39;a.b.c&#39; or &#39;x.y.z&#39; )

No problem for JSON, but how can I get a mapping in Java?

When having more fields being dynamic (link or instance based on classId) a manual way would be difficult to implement if the combination could be 2 links and 3 objects.
It also could be that a object has the same feature - a filed with a link or a instance of a class specified by classId.

The JSON response would be this:

{
"data": "abskasdkjhkjsahfkajdf-linkToResource"
}


or this:

{
"data": {
"classId": "a.b.subclass",
"detail": "some data"
}
}


or this:

{
"data": {
"classId": "a.b.subclass",
"detail": "some data"
"data2": "some-link-id",
"data3": {
"detailB": "foo",
"detailC": "some-link-id"
}
}
}



</details>


# 答案1
**得分**: 0

这是我的代码,不需要翻译:

```java
package com.allianz.clana.datamodel.http.epc.test;

import java.io.IOException;
import java.text.ParseException;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import lombok.Data;
import lombok.NoArgsConstructor;

public class JacksonTester2 {

    public static void main(String args[]) throws ParseException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        Item item2 = new Item("link");

        Stuff stuff = new Stuff();

        stuff.setItem(item2);
        stuff.setFoo("foo");

        String jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
        System.out.println(jsonStringStuff);

        Item item3 = new Item("{ \"name\":\"ID3\", \"creationDate\":\"1984-12-30\", \"rollNo\": 1 }");

        stuff.setItem(item3);
        stuff.setFoo("bar");

        jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
        System.out.println(jsonStringStuff);

    }
}

class CustomItemSerializer extends StdSerializer<Item> {
    private static final long serialVersionUID = 1L;

    public CustomItemSerializer() {
        this(null);
    }

    public CustomItemSerializer(Class<Item> t) {
        super(t);
    }

    @Override
    public void serialize(Item item, JsonGenerator generator, SerializerProvider arg2) throws IOException {
        if (item != null) {
            if (item.getItem() != null) {
                System.out.println("ItemA POJO data");
                generator.writePOJO(item.getItem());
            } else {
                System.out.println("raw data with link");
                generator.writeString(item.getRawdata());
            }
        }
    }
}

@Data
class Stuff {
    Item item;
    String foo;
}

@JsonSerialize(using = CustomItemSerializer.class)
@Data
@NoArgsConstructor
class Item {
    private String rawdata;
    @JsonIgnore
    private ItemA item;

    public Item(String rawdata) {
        this.rawdata = rawdata;
        if (rawdata.contains("{")) {
            try {
                this.item = new ObjectMapper().readerFor(ItemA.class).readValue(rawdata);
            } catch (JsonMappingException  e) {
                e.printStackTrace();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
    }
}

@Data
@NoArgsConstructor
class ItemA{
    private String name;
    private int rollNo;
    private String creationDate;

    public ItemA(String name, int rollNo, String dob) {
        this.name = name;
        this.rollNo = rollNo;
        this.creationDate = dob;
    }
}

自定义的 CustomItemSerializer 决定是打印链接还是 POJO。

英文:

Here I do have a possible solution for my problem. The logic to print the address only or the POJO relies soley in the CustomItemSerializer. So it is possible to use this without using duplicate code in controllers.

package com.allianz.clana.datamodel.http.epc.test;

import java.io.IOException;
import java.text.ParseException;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import lombok.Data;
import lombok.NoArgsConstructor;

public class JacksonTester2 {

	public static void main(String args[]) throws ParseException, JsonProcessingException {
		ObjectMapper mapper = new ObjectMapper();

		Item item2 = new Item(&quot;link&quot;);
		
		Stuff stuff = new Stuff();
		
		stuff.setItem(item2);
		stuff.setFoo(&quot;foo&quot;);
		
		String jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
		System.out.println(jsonStringStuff);

		Item item3 = new Item(&quot;{ \&quot;name\&quot;:\&quot;ID3\&quot;, \&quot;creationDate\&quot;:\&quot;1984-12-30\&quot;, \&quot;rollNo\&quot;: 1 }&quot;);
		
		stuff.setItem(item3);
		stuff.setFoo(&quot;bar&quot;);
		
		jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
		System.out.println(jsonStringStuff);
		
	}
}

class CustomItemSerializer extends StdSerializer&lt;Item&gt; {
	private static final long serialVersionUID = 1L;
	
	public CustomItemSerializer() {
		this(null);
	}
	
	public CustomItemSerializer(Class&lt;Item&gt; t) {
		super(t);
	}
	
	@Override
	public void serialize(Item item, JsonGenerator generator, SerializerProvider arg2) throws IOException {
		if (item != null) {
			if (item.getItem() != null) {
				System.out.println(&quot;ItemA POJO data&quot;);
				generator.writePOJO(item.getItem());
			} else {
				System.out.println(&quot;raw data with link&quot;);
				generator.writeString(item.getRawdata());
			}
		} 
	}
}

@Data
class Stuff {
	Item item;
	String foo;
}

@JsonSerialize(using = CustomItemSerializer.class)
@Data
@NoArgsConstructor
class Item {
	private String rawdata;
	@JsonIgnore
	private ItemA item;
	
	public Item(String rawdata) {
		this.rawdata = rawdata;
		if (rawdata.contains(&quot;{&quot;)) {
			try {
				this.item = new ObjectMapper().readerFor(ItemA.class).readValue(rawdata);
			} catch (JsonMappingException  e) {
				e.printStackTrace();
			} catch (JsonProcessingException e) {
				e.printStackTrace();
			}
		} 
	}
}

@Data
@NoArgsConstructor
class ItemA{
	private String name;
	private int rollNo;
	private String creationDate;

	public ItemA(String name, int rollNo, String dob) {
		this.name = name;
		this.rollNo = rollNo;
		this.creationDate = dob;
	}
}

The output looks like this:

raw data with link
{
  &quot;item&quot; : &quot;link&quot;,
  &quot;foo&quot; : &quot;foo&quot;
}

ItemA POJO data
{
  &quot;item&quot; : {
    &quot;name&quot; : &quot;ID3&quot;,
    &quot;rollNo&quot; : 1,
    &quot;creationDate&quot; : &quot;1984-12-30&quot;
  },
  &quot;foo&quot; : &quot;bar&quot;
}

The CustomItemSerializer decides if the link is printed or the POJO.

huangapple
  • 本文由 发表于 2023年2月7日 01:10:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75364464.html
匿名

发表评论

匿名网友

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

确定