Generate JSON sample from Java class

十年热恋 提交于 2021-02-04 15:27:06

问题


How can you generate a JSON sample with dummy data from a Java class definition? (Note: I am not asking about generating JSON from a POJO. This is something that has been asked before on stackoverflow).

What I want is to generate some sample dummy data from a Java class directly. For example you have a class like this one:

public class Reservation  {

  @ApiModelProperty(value = "")
  private Traveller leadTraveller = null;

  @ApiModelProperty(example = "2", value = "")
  private Integer sourceSystemID = null;

  @ApiModelProperty(value = "")
  private String recordLocation = null;

  @ApiModelProperty(example = "2010", value = "")
  private Integer recordLocatorYear = null;

}

And then you have a function which generates without creating a POJO a JSON string with dummy values like:

{
    "leadTraveller": {
        "firstNames": "firstNames",
        "lastName": "lastName",
        "email": "email",
        "travellerGUID": "travellerGUID",
        "travellerRefs": [{
            "customerReportingRank": 37,
            "value": "value",
            "description": "description"
        }]
    },
    "sourceSystemID": 38,
    "recordLocation": "recordLocation",
    "recordLocatorYear": 9
}

Is there a library which can do this by default?

I have tried to solve this problem using Java code with these Maven dependencies:

<dependency>
    <groupId>fctg-ngrp</groupId>
    <artifactId>model-core</artifactId>
    <version>1.0.4-SNAPSHOT-MODEL-CORE</version>
</dependency>
<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.9.2</version>
    <scope>test</scope>
</dependency>

Jackson is mainly used for verifying and formatting the output JSON.

Below is the Java code I used:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Files;
import io.vavr.API;
import io.vavr.collection.Array;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Sample utility for generating dummy JSON sample code from a JAva class directly.
 */
public class GenerateJSONFromClasses {

    private static final Random R = new Random();

    /**
     * Used to avoid infinite loops.
     */
    private static final Map<Class<?>, Integer> visited = new HashMap<>();

    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) throws IOException {
        Class<Reservation> clazz = Reservation.class;
        generateDummyJSON(clazz, args);
    }

    public static void generateDummyJSON(Class<Reservation> clazz, String... paths) throws IOException {
        StringWriter out = new StringWriter();
        try (PrintWriter writer = new PrintWriter(out)) {
            writer.println(printObj(clazz));
            JsonNode jsonNode = mapper.readTree(out.toString());
            String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
            if (paths == null || paths.length == 0) {
                System.out.println(prettyJson);
            } else {
                Array.of(paths).map(sPath -> Paths.get(sPath))
                        .map(Path::toFile)
                        .map(API.unchecked(file -> {
                            Files.write(prettyJson, file, StandardCharsets.UTF_8);
                            return file;
                        }));
            }
        }
    }

    private static String printObj(Class<?> clazz) {
        if (!visited.containsKey(clazz) || visited.get(clazz) <= 1) {
            visited.merge(clazz, 1, (integer, integer2) -> integer + integer2);
            Field[] declaredFields = clazz.getDeclaredFields();
            return "{" +
                    Array.of(declaredFields).map(field -> String.format("  \"%s\" : %s%n", field.getName(), printFieldValue(field)))
                            .collect(Collectors.joining(String.format(",%n"))) +
                    "}";
        }
        return "";
    }

    private static Object printFieldValue(Field field) {
        Class<?> fieldType = field.getType();
        if (String.class.equals(fieldType)) {
            return String.format("\"%s\"", field.getName());
        } else if (Integer.class.equals(fieldType)) {
            return R.nextInt(99) + 1;
        } else if (LocalDate.class.equals(fieldType)) {
            return String.format("\"%s\"", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        } else if (LocalDateTime.class.equals(fieldType)) {
            return String.format("\"%s\"", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")));
        } else if (OffsetDateTime.class.equals(fieldType)) {
            return String.format("\"%s\"", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")));
        } else if (Date.class.equals(fieldType)) {
            return System.currentTimeMillis();
        } else if (List.class.equals(fieldType)) {
            ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
            Class<?> clazz = (Class<?>) parameterizedType.getActualTypeArguments()[0];
            return String.format("[%s]", printObj(clazz));
        } else if (fieldType.isAssignableFrom(Number.class)) {
            return R.nextDouble() * 10.0;
        } else if (BigDecimal.class.equals(fieldType)) {
            return new BigDecimal(R.nextDouble() * 10.0);
        } else if (Boolean.class.equals(fieldType)) {
            return R.nextBoolean();
        } else {
            return printObj(fieldType);
        }
    }
}

回答1:


thanks @gil.fernandes, I have used your code with small modifications.

1) allow the same type multiple times but keep visited count to 100 to prevent infinite loops. I supposed your use case was different.

2) added java.util.Date and Enum.

3) print dummy values as the simple name of the class. In the case of enum print out "String of " and a list of its values.

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.io.Files;
    import com.jnj.na.webmethods.core.dto.MaterialWsDTO;
    import io.vavr.API;
    import io.vavr.collection.Array;

    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.ParameterizedType;
    import java.math.BigDecimal;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.OffsetDateTime;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * Sample utility for generating dummy JSON sample code from a JAva class directly.
     */
    public class GenerateJSONFromClasses {

        /**
         * Used to avoid infinite loops.
         */
        private static final Map<Class<?>, Integer> visited = new HashMap<>();

        private static final ObjectMapper mapper = new ObjectMapper();

        public static void main(String[] args) throws IOException {
            Class<MaterialWsDTO> clazz = MaterialWsDTO.class;
            generateDummyJSON(clazz, args);
        }

        public static void generateDummyJSON(Class<MaterialWsDTO> clazz, String... paths) throws IOException {
            StringWriter out = new StringWriter();
            try (PrintWriter writer = new PrintWriter(out)) {
                writer.println(printObj(clazz));
                JsonNode jsonNode = mapper.readTree(out.toString());
                String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
                if (paths == null || paths.length == 0) {
                    System.out.println(prettyJson);
                } else {
                    Array.of(paths).map(sPath -> Paths.get(sPath))
                            .map(Path::toFile)
                            .map(API.unchecked(file -> {
                                Files.write(prettyJson, file, StandardCharsets.UTF_8);
                                return file;
                            }));
                }
            }
        }

        private static String printObj(Class<?> clazz) {
            if (!visited.containsKey(clazz) || visited.get(clazz) <= 100) {
                visited.merge(clazz, 1, (integer, integer2) -> integer + integer2);
                Field[] declaredFields = clazz.getDeclaredFields();
                return "{" +
                        Array.of(declaredFields)
                                .filterNot(e->Modifier.isStatic(e.getModifiers()))
                                .map(field -> String.format("  \"%s\" : %s%n", field.getName(), printFieldValue(field)))
                                .collect(Collectors.joining(String.format(",%n"))) +
                        "}";
            }
            return "";
        }

        private static Object printFieldValue(Field field) {
            Class<?> fieldType = field.getType();
            if (String.class.equals(fieldType)) {
                return name(fieldType);
            } else if (Integer.class.equals(fieldType)) {
                return name(fieldType);
            } else if (Enum.class.isAssignableFrom(fieldType)) {
                return printEnum(fieldType);
            } else if (Double.class.equals(fieldType)) {
                return name(fieldType);
            } else if (LocalDate.class.equals(fieldType)) {
                return name(fieldType);
            } else if (LocalDateTime.class.equals(fieldType)) {
                return name(fieldType);
            } else if (OffsetDateTime.class.equals(fieldType)) {
                return name(fieldType);
            } else if (Date.class.equals(fieldType)) {
                return name(fieldType);
            } else if (List.class.equals(fieldType)) {
                ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                Class<?> clazz = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                return String.format("[%s]", printObj(clazz));
            } else if (fieldType.isAssignableFrom(Number.class)) {
                return name(fieldType);
            } else if (BigDecimal.class.equals(fieldType)) {
                return name(fieldType);
            } else if (Boolean.class.equals(fieldType)) {
                return name(fieldType);
            } else {
                return printObj(fieldType);
            }
        }

        private static String printEnum(Class<?> fieldType) {
            Field[] declaredFields = fieldType.getDeclaredFields();
            return "\"String of " + Arrays.stream(declaredFields)
                    .filter(field -> field.getType()==fieldType)
                    .map(Field::getName)
                    .collect(Collectors.joining(",")) +
                   "\"";
        }

        private static Object name(Class<?> fieldType) {
            return "\""+fieldType.getSimpleName()+"\"";
        }
    }



回答2:


You can try this code: this will use gson lib to translate a object o json string

public static <T> String convertObjectToJSONString(Object obj) {
    Gson gson = new GsonBuilder().serializeNulls().create();
    if (obj == null) {         
        return null;
    }
    return gson.toJson(obj);
}


来源:https://stackoverflow.com/questions/51673259/generate-json-sample-from-java-class

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!