datafaker-net / datafaker

Generating fake data for the JVM (Java, Kotlin, Groovy) has never been easier!
https://www.datafaker.net
Apache License 2.0
1.2k stars 176 forks source link

How to generate JSON with different data providers #346

Closed panilya closed 2 years ago

panilya commented 2 years ago

1)

[{
    "firstName": "Tesha", 
    "lastName": "Wyman", 
    "address": "Algeria"
}]

2)

[{
    "firstName": "Brenton", 
    "lastName": "Schumm", 
    "address": ["Saint Pierre and Miquelon"]
}]

Currently, I found only way to generate JSON with different providers via approach like this .set("phones", name -> faker.collection(() -> faker.phoneNumber().phoneNumber()).len(2).build().get()), but this way outputs as a list (code snippet 2.). Is it possible to generate JSONs as in code snippet 1.?

"address": "Algeria" and "address": ["Saint Pierre and Miquelon"], I need to achieve the first result.

snuyanzin commented 2 years ago

here are 2 pojos: FirstPOJO and SecondPOJO which could help to generate JSON for your first and second cases


import net.datafaker.fileformats.Format;

import java.util.stream.Collectors;

public class Issue346 {
  private static class FirstPOJO {
    private final Name name;
    private final Address address;

    public FirstPOJO(Name name, Address address) {
      this.name = name;
      this.address = address;
    }

    public Name getName() {
      return name;
    }

    public Address getAddress() {
      return address;
    }
  }

  private static class SecondPOJO {
    private final Name name;
    private final FakeCollection<Address> addresses;

    public SecondPOJO(Name name, FakeCollection<Address> addresses) {
      this.name = name;
      this.addresses = addresses;
    }

    public Name getName() {
      return name;
    }

    public FakeCollection<Address> getAddresses() {
      return addresses;
    }
  }

  public static void main(String[] args) {
    Faker faker = new Faker();

    System.out.println(
        Format.toJson(
                faker.collection(() -> new FirstPOJO(faker.name(), faker.address())).len(2).build())
            .set("firstName", firstPOJO -> firstPOJO.getName().firstName())
            .set("lastname", firstPOJO -> firstPOJO.getName().lastName())
            .set("address", firstPOJO -> firstPOJO.getAddress().country())
            .build()
            .generate());

    System.out.println();

    System.out.println(
        Format.toJson(
                faker
                    .collection(
                        () ->
                            new SecondPOJO(
                                faker.name(), faker.collection(faker::address).len(2).build()))
                    .len(2)
                    .build())
            .set("firstName", secondPOJO -> secondPOJO.getName().firstName())
            .set("lastname", secondPOJO -> secondPOJO.getName().lastName())
            .set(
                "address",
                secondPOJO ->
                    secondPOJO.getAddresses().get().stream()
                        .map(Address::country)
                        .collect(Collectors.toList()))
            .build()
            .generate());
  }
}

e.g.

[{"firstName": "Quincy", "lastname": "Bruen", "address": "Serbia"},
{"firstName": "Jada", "lastname": "Barton", "address": "Cambodia"}]

[{"firstName": "Jae", "lastname": "Moore", "address": ["Austria", "Colombia"]},
{"firstName": "Richie", "lastname": "Muller", "address": ["Iceland", "United Arab Emirates"]}]

In case you need other number of elements, adjust number in len(2) accordingly

bodiam commented 2 years ago

@panilya would it make sense to add this to the documentation? Or maybe a tutorial?

panilya commented 2 years ago

@bodiam I think yes, because in the most cases you want to generate data with different data providers, but docs doesn't tell anything how to do it (except append different provider as a list, which is not what you want in most cases). Also, as for me, generating data in CSV format has more obvious API. I'm curious if it's possible to implement something similar for JSON format. I mean to pass just a list of JSONColumns that wrap needed providers and receive data in JSON format. It would be cool, if this can be achieved.

snuyanzin commented 2 years ago

CSV is much simpler because it does not allow nested data. In JSON it's possible to generate a weird structure like this

{"A":[{"B":{"C": "D", "E":[{"F":"E"}]}}]}

and we should support it

snuyanzin commented 2 years ago

By the way @panilya sometimes the similar issues could be faced even with CSV and POJOs could also help there For more details have a look at datafaker's article at dzone https://dzone.com/articles/datafaker-a-solid-alternative-to-using-production especially Exporting Data With Some Constraints part

snuyanzin commented 2 years ago

@panilya if we are talking about only some simple cases like this

{"firstName": "Clemencia", "lastName": "Collier", "address": "Nauru"}

then the code could be much simpler like

    Faker faker = new Faker();

    System.out.println(Format.toJson()
        .set("firstName", () -> faker.name().firstName())
        .set("lastName", () -> faker.name().lastName())
        .set("address", () -> faker.address().country())
        .build().generate());
panilya commented 2 years ago

@snuyanzin I already did what I wanted to do, thanks.

As for me, adding this to documentation will help a lot. Because it's not obvious at first sight.

    Faker faker = new Faker();

    System.out.println(Format.toJson()
        .set("firstName", () -> faker.name().firstName())
        .set("lastName", () -> faker.name().lastName())
        .set("address", () -> faker.address().country())
        .build().generate());
snuyanzin commented 2 years ago

good if so, if it is helpful feel free to add this however would make sense to highlight that this approach will not work for complex JSON generation

bodiam commented 2 years ago

@panilya I think this can be closed right? If not, please let me know