svn commit: r1862206 [1/2] - in /ofbiz/ofbiz-framework/trunk: ./ applications/commonext/webapp/ofbizsetup/WEB-INF/ applications/commonext/widget/ofbizsetup/ framework/entity/src/main/java/org/apache/ofbiz/entity/util/ framework/webtools/config/ framewo...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1862206 [1/2] - in /ofbiz/ofbiz-framework/trunk: ./ applications/commonext/webapp/ofbizsetup/WEB-INF/ applications/commonext/widget/ofbizsetup/ framework/entity/src/main/java/org/apache/ofbiz/entity/util/ framework/webtools/config/ framewo...

jleroux@apache.org
Author: jleroux
Date: Thu Jun 27 11:49:22 2019
New Revision: 1862206

URL: http://svn.apache.org/viewvc?rev=1862206&view=rev
Log:
Implemented: JSON entity data import and export utility
(OFBIZ-10966)

Currently, we support import/export entity data in XML format.
  Nowadays JSON is widely used in industry, we can have support for JSON format
  which looks quite similar to XML support.

Here is example of XML data and it's JSON version
<Party partyId="123456" partyTypeId="PERSON" statusId="PARTY_ENABLED"/>
{“Party”: {"partyId":"123456","partyTypeId":"PERSON","statusId":"PARTY_ENABLED”}}

Design Proposal

We can write entityImportJson and entityImportDirJson services for importing
JSON from screen and directory respectively.
And the entityExportAllJson service for exporting entity data in JSON.

Import Design
 The import service will perform following operations:
 1.) Validate the input JSON data
 2.) On successful validation, convert JSON to OFBiz's entity model
     (GenericValue)
 3.) The GenericValue will be inserted in database by some handler class for e.g
     we can write JsonDataHandler, it will convert given JSON to
     List<GenericValue>, and finally write it to database
     (Similar pattern is used in XML import).

Export Design
 Based on existing XML pattern the writeXmlText method of GenericEntity class
 write the exported data in XML format.
 In the similar way, we can implement writeJsonText to export data in JSON format.

jleroux: I fixed 2 trivials things and at my request in last patch Jayansh added
"JSON Data Export All" and "JSON Data Import Dir

Thanks: Jayansh Shinde

Added:
    ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/src/main/java/org/apache/ofbiz/webtools/EntityJsonEvents.java   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/src/main/java/org/apache/ofbiz/webtools/EntityJsonHelper.java   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/template/entity/EntityExportAllJson.ftl   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/template/entity/EntityImportDirJson.ftl   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/template/entity/EntityImportJson.ftl   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webtools/template/entity/JsonDsDump.ftl   (with props)
Modified:
    ofbiz/ofbiz-framework/trunk/applications/commonext/webapp/ofbizsetup/WEB-INF/controller.xml
    ofbiz/ofbiz-framework/trunk/applications/commonext/widget/ofbizsetup/CommonScreens.xml
    ofbiz/ofbiz-framework/trunk/build.gradle
    ofbiz/ofbiz-framework/trunk/framework/webtools/config/WebtoolsUiLabels.xml
    ofbiz/ofbiz-framework/trunk/framework/webtools/servicedef/services.xml
    ofbiz/ofbiz-framework/trunk/framework/webtools/src/main/java/org/apache/ofbiz/webtools/WebToolsServices.java
    ofbiz/ofbiz-framework/trunk/framework/webtools/template/Main.ftl
    ofbiz/ofbiz-framework/trunk/framework/webtools/webapp/webtools/WEB-INF/controller.xml
    ofbiz/ofbiz-framework/trunk/framework/webtools/widget/EntityScreens.xml
    ofbiz/ofbiz-framework/trunk/framework/webtools/widget/Menus.xml

Modified: ofbiz/ofbiz-framework/trunk/applications/commonext/webapp/ofbizsetup/WEB-INF/controller.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/commonext/webapp/ofbizsetup/WEB-INF/controller.xml?rev=1862206&r1=1862205&r2=1862206&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/commonext/webapp/ofbizsetup/WEB-INF/controller.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/commonext/webapp/ofbizsetup/WEB-INF/controller.xml Thu Jun 27 11:49:22 2019
@@ -58,6 +58,17 @@
         <response name="success" type="view" value="EntityExportAll"/>
         <response name="error" type="view" value="EntityExportAll"/>
     </request-map>
+    <request-map uri="EntityExportAllJson">
+        <security https="true" auth="true"/>
+        <response name="success" type="view" value="EntityExportAllJson"/>
+        <response name="error" type="view" value="EntityExportAllJson"/>
+    </request-map>
+    <request-map uri="entityExportAllJson">
+        <security https="true" auth="true"/>
+        <event type="service" path="" invoke="entityExportAllJson"/>
+        <response name="success" type="view" value="EntityExportAllJson"/>
+        <response name="error" type="view" value="EntityExportAllJson"/>
+    </request-map>
     
     <!-- ================ Product Store Requests ============= -->
     <request-map uri="FindProductStore">
@@ -206,6 +217,7 @@
     <view-map name="EditProductStore" type="screen" page="component://commonext/widget/ofbizsetup/SetupScreens.xml#EditProductStore"/>
 
     <view-map name="EntityExportAll" type="screen" page="component://commonext/widget/ofbizsetup/CommonScreens.xml#EntityExportAll"/>
+    <view-map name="EntityExportAllJson" type="screen" page="component://commonext/widget/ofbizsetup/CommonScreens.xml#EntityExportAllJson"/>
 
     <view-map name="FindWebSite" type="screen" page="component://commonext/widget/ofbizsetup/SetupScreens.xml#FindWebSite"/>
     <view-map name="EditWebSite" type="screen" page="component://commonext/widget/ofbizsetup/SetupScreens.xml#EditWebSite"/>

Modified: ofbiz/ofbiz-framework/trunk/applications/commonext/widget/ofbizsetup/CommonScreens.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/commonext/widget/ofbizsetup/CommonScreens.xml?rev=1862206&r1=1862205&r2=1862206&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/commonext/widget/ofbizsetup/CommonScreens.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/commonext/widget/ofbizsetup/CommonScreens.xml Thu Jun 27 11:49:22 2019
@@ -153,4 +153,26 @@
             </widgets>
         </section>
     </screen>
+    <screen name="EntityExportAllJson">
+        <section>
+            <actions>
+                <set field="titleProperty" value="PageTitleEntityExportAllJson"/>
+                <set field="tabButtonItem" value="entityExportAllJson"/>
+                <set field="parameters.TRANSACTION_TIMEOUT" value="7200"/>
+                <set field="results" from-field="parameters.results"/>
+                <set field="headerItem" value="export"/>
+            </actions>
+            <widgets>
+                <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
+                    <decorator-section name="body">
+                        <screenlet title="${uiLabelMap.PageTitleEntityExportAllJson}">
+                            <platform-specific>
+                                <html><html-template location="component://webtools/template/entity/EntityExportAllJson.ftl"/></html>
+                            </platform-specific>
+                        </screenlet>
+                    </decorator-section>
+                </decorator-screen>
+            </widgets>
+        </section>
+    </screen>
 </screens>

Modified: ofbiz/ofbiz-framework/trunk/build.gradle
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/build.gradle?rev=1862206&r1=1862205&r2=1862206&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/build.gradle (original)
+++ ofbiz/ofbiz-framework/trunk/build.gradle Thu Jun 27 11:49:22 2019
@@ -209,6 +209,7 @@ dependencies {
     implementation 'wsdl4j:wsdl4j:1.6.3'
     implementation 'org.jsoup:jsoup:1.11.3'
     implementation 'io.jsonwebtoken:jjwt:0.9.1'
+    implementation 'org.json:json:20140107'
     testImplementation 'org.mockito:mockito-core:2.27.0'
     runtimeOnly 'javax.xml.soap:javax.xml.soap-api:1.4.0'
     runtimeOnly 'de.odysseus.juel:juel-spi:2.2.7'

Added: ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java?rev=1862206&view=auto
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java (added)
+++ ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java Thu Jun 27 11:49:22 2019
@@ -0,0 +1,598 @@
+package org.apache.ofbiz.entity.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ofbiz.base.lang.JSON;
+import org.apache.ofbiz.base.util.Base64;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.GenericEntityNotFoundException;
+import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.datasource.GenericHelper;
+import org.apache.ofbiz.entity.eca.EntityEcaHandler;
+import org.apache.ofbiz.entity.model.ModelEntity;
+import org.apache.ofbiz.entity.model.ModelField;
+import org.apache.ofbiz.entity.transaction.GenericTransactionException;
+import org.apache.ofbiz.entity.transaction.TransactionUtil;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public class EntityJsonReader {
+    public static final String module = EntitySaxReader.class.getName();
+    public static final int DEFAULT_TX_TIMEOUT = 7200;
+    private Delegator delegator;
+    private EntityEcaHandler<?> ecaHandler = null;
+    private long numberRead = 0;
+    private long numberCreated = 0;
+    private long numberUpdated = 0;
+    private long numberReplaced = 0;
+    private long numberDeleted = 0;
+    private long numberSkipped = 0;
+
+    private int valuesPerWrite = 100;
+    private int valuesPerMessage = 1000;
+    private int transactionTimeout = 7200;
+    private boolean useTryInsertMethod = false;
+    private boolean maintainTxStamps = false;
+    private boolean createDummyFks = false;
+    private boolean checkDataOnly = false;
+    private boolean continueOnFail = false;
+
+    private List<Object> messageList = null;
+
+    private List<GenericValue> valuesToCreate = new ArrayList<>(valuesPerWrite);
+    private List<GenericValue> valuesToDelete = new ArrayList<>(valuesPerWrite);
+    private List<GenericValue> valuesToUpdate = new ArrayList<>(valuesPerWrite);
+
+    /**TODO need to evaluate how placeholders are going to be used in json data*/
+    private Map<String, Object> placeholderValues = null; //contains map of values for corresponding placeholders (eg. ${key}) in the entity xml data file.
+
+    protected EntityJsonReader() {
+    }
+
+    public EntityJsonReader(Delegator delegator, int transactionTimeout) {
+        this.delegator = delegator.cloneDelegator();
+        this.transactionTimeout = transactionTimeout;
+    }
+
+    public EntityJsonReader(Delegator delegator) {
+        this(delegator, DEFAULT_TX_TIMEOUT);
+    }
+
+    public int getTransactionTimeout() {
+        return this.transactionTimeout;
+    }
+
+    public void setUseTryInsertMethod(boolean value) {
+        this.useTryInsertMethod = value;
+    }
+
+    public void setTransactionTimeout(int transactionTimeout) throws GenericTransactionException {
+        if (this.transactionTimeout != transactionTimeout) {
+            TransactionUtil.setTransactionTimeout(transactionTimeout);
+            this.transactionTimeout = transactionTimeout;
+        }
+
+    }
+
+    public void setMaintainTxStamps(boolean maintainTxStamps) {
+        this.maintainTxStamps = maintainTxStamps;
+    }
+
+    public void setCreateDummyFks(boolean createDummyFks) {
+        this.createDummyFks = createDummyFks;
+    }
+
+    public void setCheckDataOnly(boolean checkDataOnly) {
+        this.checkDataOnly = checkDataOnly;
+    }
+
+    public void setContinueOnFail(boolean continueOnFail) {
+        this.continueOnFail = continueOnFail;
+    }
+
+    public void setPlaceholderValues(Map<String, Object> placeholderValues) {
+        this.placeholderValues = placeholderValues;
+    }
+
+    public List<Object> getMessageList() {
+        if (this.checkDataOnly && this.messageList == null) {
+            this.messageList = new LinkedList();
+        }
+
+        return this.messageList;
+    }
+
+    public void setDisableEeca(boolean disableEeca) {
+        if (disableEeca) {
+            if (this.ecaHandler == null) {
+                this.ecaHandler = this.delegator.getEntityEcaHandler();
+            }
+
+            this.delegator.setEntityEcaHandler((EntityEcaHandler) null);
+        } else if (this.ecaHandler != null) {
+            this.delegator.setEntityEcaHandler(this.ecaHandler);
+        }
+
+    }
+
+    public long parse(String content) throws IOException {
+        if (content == null) {
+            Debug.logWarning("content was null, doing nothing", module);
+            return 0L;
+        } else {
+            return this.convertJsonAndWriteValues(content);
+        }
+    }
+
+    public long parse(URL location) throws IOException {
+        if (location == null) {
+            Debug.logWarning("location URL was null, doing nothing", module);
+            return 0L;
+        } else {
+            Debug.logImportant("Beginning import from URL: " + location.toExternalForm(), module);
+            long numberRead = 0L;
+            InputStream is = location.openStream();
+            numberRead = this.parse(is, location.toString());
+            return numberRead;
+        }
+    }
+
+    private long parse(InputStream is, String docDescription) throws IOException {
+        JSON json = JSON.from(is);
+        return this.convertJsonAndWriteValues(json.toString());
+    }
+
+    private long convertJsonAndWriteValues(String jsonString) throws IOException {
+        this.numberRead = 0L;
+        String _prefix = "";
+        JSONArray jsonArray = new JSONArray(jsonString);
+        int length = jsonArray.length();
+
+        for (int jsonIndex = 0; jsonIndex < length; ++jsonIndex) {
+            JSONObject jsonObject = jsonArray.getJSONObject(jsonIndex);
+            Map<String, Map<String, Object>> flatJson = new HashMap<String, Map<String, Object>>();
+            Iterator iterator = jsonObject.keySet().iterator();
+
+            while (iterator.hasNext()) {
+                String key = iterator.next().toString();
+                /**TODO use something else instead of if-else */
+                if (key.equals("create")) {
+                    action(jsonObject.get(key), "create");
+                } else if (key.equals("create-update")) {
+                    action(jsonObject.get(key), "createUpdate");
+                } else if (key.equals("create-replace")) {
+                    action(jsonObject.get(key), "createReplace");
+                } else if (key.equals("delete")) {
+                    action(jsonObject.get(key), "delete");
+                } else {
+                    /**TODO replace this block with createUpdate method*/
+                    /*Object value = jsonObject.get(key);
+                    if (value != null && !value.equals("null") && value instanceof JSONObject) {
+                        flatJson.put(_prefix + key, this.iterateJSONObject((JSONObject) value));
+                        ModelEntity modelEntity = this.delegator.getModelEntity(key);
+                        GenericValue entityVal = GenericValue.create(this.delegator, modelEntity,
+                                this.iterateJSONObject((JSONObject) value));
+                        if (UtilValidate.isNotEmpty(entityVal)) {
+                            this.valuesToCreate.add(entityVal);
+                        }
+                    }*/
+                    createUpdate(jsonObject);
+                }
+            }
+        }
+
+        this.numberRead = this.writeValues();
+        return this.numberRead;
+    }
+
+    private List<Map<String, Object>> iterateJsonEntityData(Object jsonData) {
+        List<Map<String, Object>> genericMapList = new LinkedList<Map<String, Object>>();
+        if (jsonData instanceof JSONArray) {
+            JSONArray jsonArray = (JSONArray) jsonData;
+            int length = jsonArray.length();
+            for (int jsonIndex = 0; jsonIndex < length; ++jsonIndex) {
+                JSONObject jsonObject = jsonArray.getJSONObject(jsonIndex);
+                Map<String, Object> genericMap = iterateJSONObject(jsonObject);
+                if (UtilValidate.isNotEmpty(genericMap)) {
+                    genericMapList.add(genericMap);
+                }
+            }
+        } else if (jsonData instanceof JSONObject) {
+            JSONObject jsonObject = (JSONObject) jsonData;
+            Map<String, Object> genericMap = iterateJSONObject(jsonObject);
+            if (UtilValidate.isNotEmpty(genericMap)) {
+                genericMapList.add(genericMap);
+            }
+        }
+        return genericMapList;
+    }
+
+    private Map<String, Object> iterateJSONObject(JSONObject jsonObj) {
+        Map<String, Object> mapObj = new HashMap<String, Object>();
+        Iterator iterator = jsonObj.keySet().iterator();
+        while (iterator.hasNext()) {
+            String keyStr = (String) iterator.next();
+            Object keyvalue = jsonObj.get(keyStr);
+            if (keyvalue instanceof String) {
+                String keyValStr = org.apache.commons.text.StringEscapeUtils.unescapeJson((String) keyvalue);
+                mapObj.put(keyStr, keyValStr);
+            } else {
+                mapObj.put(keyStr, keyvalue);
+            }
+        }
+        return mapObj;
+    }
+
+    private long create(JSONObject jsonObject) throws IOException {
+        Iterator iterator = jsonObject.keySet().iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next().toString();
+            Object value = jsonObject.get(key);
+            if (UtilValidate.isNotEmpty(value)) {
+                List<Map<String, Object>> genericMapList = this.iterateJsonEntityData(value);
+                for (Map<String, Object> keyValPair : genericMapList) {
+                    try {
+                        ModelEntity modelEntity = this.delegator.getModelEntity(key);
+                        GenericValue currentValue = delegator.makeValue(key, keyValPair);
+                        if (this.maintainTxStamps) {
+                            currentValue.setIsFromEntitySync(true);
+                        }
+                        GenericHelper helper = delegator.getEntityHelper(currentValue.getEntityName());
+                        if (UtilValidate.isNotEmpty(currentValue)) {
+                            boolean exist = true;
+                            if (currentValue.containsPrimaryKey()) {
+                                try {
+                                    helper.findByPrimaryKey(currentValue.getPrimaryKey());
+                                } catch (GenericEntityNotFoundException e) {
+                                    exist = false;
+                                }
+                            }
+                            if (!exist) {
+                                if (this.useTryInsertMethod && !this.checkDataOnly) {
+                                    currentValue.create();
+                                } else {
+                                    this.valuesToCreate.add(currentValue);
+                                }
+                                this.numberCreated++;
+                            }//if pk exist ignore it.
+                        }
+                    } catch (Exception e) {
+                        if (continueOnFail) {
+                            Debug.logError(e, module);
+                        } else {
+                            throw new IOException(e);
+                        }
+                    }
+                }
+            }
+        }
+        return this.numberCreated;
+    }
+
+    private long createUpdate(JSONObject jsonObject) throws IOException {
+        Iterator iterator = jsonObject.keySet().iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next().toString();
+            Object value = jsonObject.get(key);
+            if (UtilValidate.isNotEmpty(value)) {
+                List<Map<String, Object>> genericMapList = this.iterateJsonEntityData(value);
+                for (Map<String, Object> keyValPair : genericMapList) {
+                    try {
+                        GenericValue currentValue = delegator.makeValue(key);
+                        if (currentValue != null) {
+                            ModelEntity modelEntity = currentValue.getModelEntity();
+                            List<String> pkFields = modelEntity.getPkFieldNames();
+                            for (String pkField : pkFields) {
+                                ModelField modelField = modelEntity.getField(pkField);
+                                Object pkFieldValue = keyValPair.get(pkField);
+                                String type = modelField.getType();
+                                if (type != null && "blob".equals(type)) {
+                                    byte[] binData = Base64.base64Decode((pkFieldValue.toString()).getBytes());
+                                    currentValue.setBytes(pkField, binData);
+                                } else {
+                                    currentValue.setString(pkField, pkFieldValue.toString());
+                                }
+                            }
+
+                            GenericHelper helper = delegator.getEntityHelper(currentValue.getEntityName());
+
+                            boolean exist = true;
+                            if (currentValue.containsPrimaryKey()) {
+                                try {
+                                    helper.findByPrimaryKey(currentValue.getPrimaryKey());
+                                } catch (GenericEntityNotFoundException e) {
+                                    exist = false;
+                                }
+                            }
+                            for (Map.Entry<String, Object> entry : keyValPair.entrySet()) {
+                                String currentFieldName = entry.getKey();
+                                Object currentFieldValue = entry.getValue();
+                                if (currentFieldName != null && !pkFields.contains(currentFieldName)) {
+                                    if (currentValue.getModelEntity().isField(currentFieldName)) {
+                                        if (UtilValidate.isNotEmpty(currentFieldValue)) {
+                                            ModelField modelField = modelEntity.getField(currentFieldName);
+                                            String type = modelField.getType();
+                                            if (type != null && "blob".equals(type)) {
+                                                byte[] binData = Base64.base64Decode(currentFieldValue.toString().getBytes());
+                                                currentValue.setBytes(currentFieldName, binData);
+                                            } else {
+                                                currentValue.setString(currentFieldName, currentFieldValue.toString());
+                                            }
+                                        }
+                                    } else {
+                                        Debug.logWarning("Ignoring invalid field name [" + currentFieldName + "] found for the entity: "
+                                                + currentValue.getEntityName() + " with value=" + currentFieldValue.toString(), module);
+                                    }
+                                }
+                            }
+                            if (exist) {
+                                this.valuesToUpdate.add(currentValue);
+                            } else {
+                                // Not sure about this!
+                                //if (this.useTryInsertMethod && !this.checkDataOnly) {
+                                //    currentValue.create();
+                                //} else {
+                                this.valuesToCreate.add(currentValue);
+                                //}
+
+                            }
+                            if (this.maintainTxStamps) {
+                                currentValue.setIsFromEntitySync(true);
+                            }
+                            this.numberUpdated++;
+                        }
+                    } catch (Exception e) {
+                        if (continueOnFail) {
+                            Debug.logError(e, module);
+                        } else {
+                            throw new IOException(e);
+                        }
+                    }
+                }
+            }
+        }
+        return this.numberUpdated;
+    }
+
+    private long createReplace(JSONObject jsonObject) throws IOException {
+        Iterator iterator = jsonObject.keySet().iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next().toString();
+            Object value = jsonObject.get(key);
+            if (UtilValidate.isNotEmpty(value)) {
+                List<Map<String, Object>> genericMapList = this.iterateJsonEntityData(value);
+                for (Map<String, Object> keyValPair : genericMapList) {
+                    try {
+                        GenericValue currentValue = this.delegator.makeValue(key);
+                        if (this.maintainTxStamps) {
+                            currentValue.setIsFromEntitySync(true);
+                        }
+                        ModelEntity modelEntity = currentValue.getModelEntity();
+                        List<String> pkFields = modelEntity.getPkFieldNames();
+                        if (currentValue != null) {
+                            for (String pkField : pkFields) {
+                                ModelField modelField = modelEntity.getField(pkField);
+                                Object pkFieldValue = keyValPair.get(pkField);
+                                String type = modelField.getType();
+                                if (type != null && "blob".equals(type)) {
+                                    byte[] binData = Base64.base64Decode((pkFieldValue.toString()).getBytes());
+                                    currentValue.setBytes(pkField, binData);
+                                } else {
+                                    currentValue.setString(pkField, pkFieldValue.toString());
+                                }
+                            }
+
+                            GenericHelper helper = delegator.getEntityHelper(currentValue.getEntityName());
+
+                            boolean exist = true;
+                            if (currentValue.containsPrimaryKey()) {
+                                try {
+                                    helper.findByPrimaryKey(currentValue.getPrimaryKey());
+                                } catch (GenericEntityNotFoundException e) {
+                                    exist = false;
+                                }
+                            } else {
+                                if (modelEntity.getPksSize() == 1) {
+                                    ModelField modelField = currentValue.getModelEntity().getOnlyPk();
+                                    String newSeq = delegator.getNextSeqId(currentValue.getEntityName());
+                                    currentValue.setString(modelField.getName(), newSeq);
+                                } else {
+                                    throw new IOException("Cannot store value with incomplete primary key with more than 1 primary key field: " + currentValue);
+                                }
+                            }
+
+                            ModelEntity currentEntity = currentValue.getModelEntity();
+                            List<String> absentFields = currentEntity.getNoPkFieldNames();
+                            absentFields.removeAll(currentEntity.getAutomaticFieldNames());
+
+                            for (Map.Entry<String, Object> entry : keyValPair.entrySet()) {
+                                String currentFieldName = entry.getKey();
+                                Object currentFieldValue = entry.getValue();
+                                if (UtilValidate.isNotEmpty(currentFieldName) && !pkFields.contains(currentFieldName)) {
+                                    if (modelEntity.isField(currentFieldName)) {
+                                        if (UtilValidate.isNotEmpty(currentFieldValue)) {
+                                            ModelField modelField = modelEntity.getField(currentFieldName);
+                                            String type = modelField.getType();
+                                            if (type != null && "blob".equals(type)) {
+                                                byte[] binData = Base64.base64Decode(((String) currentFieldValue).getBytes());
+                                                currentValue.setBytes(currentFieldName, binData);
+                                            } else {
+                                                currentValue.setString(currentFieldName, currentFieldValue.toString());
+                                            }
+                                            absentFields.remove(currentFieldName);
+                                        }
+                                    } else {
+                                        Debug.logWarning("Ignoring invalid field name [" + currentFieldName + "] found for the entity: "
+                                                + currentValue.getEntityName() + " with value=" + currentFieldValue.toString(), module);
+                                    }
+                                }
+                            }
+                            if (absentFields != null) {
+                                for (String fieldName : absentFields) {
+                                    currentValue.set(fieldName, null);
+                                }
+                            }
+                            if (exist) {
+                                this.valuesToUpdate.add(currentValue);
+                            } else {
+                                // Not sure about this!
+                                //if (this.useTryInsertMethod && !this.checkDataOnly) {
+                                //    currentValue.create();
+                                //} else {
+                                this.valuesToCreate.add(currentValue);
+                                //}
+                            }
+                            if (this.maintainTxStamps) {
+                                currentValue.setIsFromEntitySync(true);
+                            }
+                            this.numberReplaced++;
+                        }
+                    } catch (Exception e) {
+                        if (continueOnFail) {
+                            Debug.logError(e, module);
+                        } else {
+                            throw new IOException(e);
+                        }
+                    }
+                }
+            }
+        }
+        return this.numberReplaced;
+    }
+
+    private long delete(JSONObject jsonObject) throws IOException {
+        Iterator iterator = jsonObject.keySet().iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next().toString();
+            Object value = jsonObject.get(key);
+            if (UtilValidate.isNotEmpty(value)) {
+                List<Map<String, Object>> genericMapList = this.iterateJsonEntityData(value);
+                for (Map<String, Object> keyValPair : genericMapList) {
+                    try {
+                        ModelEntity modelEntity = this.delegator.getModelEntity(key);
+                        GenericValue currentValue = delegator.makeValue(key, keyValPair);
+                        if (this.maintainTxStamps) {
+                            currentValue.setIsFromEntitySync(true);
+                        }
+                        GenericHelper helper = delegator.getEntityHelper(key);
+                        if (currentValue != null) {
+                            boolean exist = true;
+                            if (currentValue.containsPrimaryKey()) {
+                                try {
+                                    helper.findByPrimaryKey(currentValue.getPrimaryKey());
+                                } catch (GenericEntityNotFoundException e) {
+                                    exist = false;
+                                }
+                            }
+                            if (exist) {
+                                if (this.useTryInsertMethod && !this.checkDataOnly) {
+                                    currentValue.remove();
+                                } else {
+                                    this.valuesToDelete.add(currentValue);
+                                }
+                                this.numberDeleted++;
+                            }//if pk exist ignore it.
+                        }
+                    } catch (Exception e) {
+                        if (continueOnFail) {
+                            Debug.logError(e, module);
+                        } else {
+                            throw new IOException(e);
+                        }
+                    }
+                }
+            }
+        }
+        return this.numberDeleted;
+    }
+
+    private long action(Object jsonData, String actionName) throws IOException {
+        java.lang.reflect.Method method;
+        try {
+            method = this.getClass().getDeclaredMethod(actionName, JSONObject.class);
+            if (jsonData instanceof JSONArray) {
+                JSONArray jsonArray = (JSONArray) jsonData;
+                int length = jsonArray.length();
+                for (int jsonIndex = 0; jsonIndex < length; ++jsonIndex) {
+                    JSONObject jsonObject = jsonArray.getJSONObject(jsonIndex);
+                    method.invoke(this, jsonObject);
+                }
+            } else if (jsonData instanceof JSONObject) {
+                method.invoke(this, (JSONObject) jsonData);
+            }
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+        return this.numberCreated;
+    }
+
+    private long writeValues() {
+        this.numberRead = 0L;
+
+        try {
+            boolean beganTransaction = false;
+            if (this.transactionTimeout > -1) {
+                beganTransaction = TransactionUtil.begin(this.transactionTimeout);
+                Debug.logImportant("Transaction Timeout set to " + this.transactionTimeout / 3600 + " hours ("
+                        + this.transactionTimeout + " seconds)", module);
+            }
+
+            try {
+                if (!this.valuesToCreate.isEmpty()) {
+                    this.writeValues(this.valuesToCreate);
+                    numberRead = numberRead + this.valuesToCreate.size();
+                    this.valuesToCreate.clear();
+                }
+
+                if (!this.valuesToDelete.isEmpty()) {
+                    this.delegator.removeAll(this.valuesToDelete);
+                    numberRead = numberRead + this.valuesToDelete.size();
+                    this.valuesToDelete.clear();
+                }
+                if (!this.valuesToUpdate.isEmpty()) {
+                    this.writeValues(this.valuesToUpdate);
+                    numberRead = numberRead + this.valuesToUpdate.size();
+                    this.valuesToUpdate.clear();
+                }
+
+                TransactionUtil.commit(beganTransaction);
+            } catch (IllegalArgumentException | GenericEntityException e1) {
+                String errMsg = "An error occurred saving the data, rolling back transaction (" + beganTransaction
+                        + ")";
+                Debug.logError(e1, errMsg, module);
+                TransactionUtil.rollback(beganTransaction, errMsg, e1);
+            }
+        } catch (GenericTransactionException e) {
+            Debug.logError(e, "A transaction error occurred reading data", module);
+        }
+
+        if (Debug.verboseOn()) {
+            Debug.logVerbose("  Detail created : " + this.numberCreated + ", skipped : " + this.numberSkipped
+                    + ", updated : " + this.numberUpdated + ", replaced : " + this.numberReplaced + ", deleted : "
+                    + this.numberDeleted, module);
+        }
+
+        return this.numberRead;
+    }
+
+    private void writeValues(List<GenericValue> valuesToWrite) throws GenericEntityException {
+        if (this.checkDataOnly) {
+            EntityDataAssert.checkValueList(valuesToWrite, this.delegator, this.getMessageList());
+        } else {
+            this.delegator.storeAll(valuesToWrite, new EntityStoreOptions(this.createDummyFks));
+        }
+
+    }
+}

Propchange: ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/ofbiz-framework/trunk/framework/entity/src/main/java/org/apache/ofbiz/entity/util/EntityJsonReader.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: ofbiz/ofbiz-framework/trunk/framework/webtools/config/WebtoolsUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webtools/config/WebtoolsUiLabels.xml?rev=1862206&r1=1862205&r2=1862206&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webtools/config/WebtoolsUiLabels.xml (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webtools/config/WebtoolsUiLabels.xml Thu Jun 27 11:49:22 2019
@@ -98,6 +98,15 @@
         <value xml:lang="zh">没有指定的文件名/网址或完整的XML文档,什么也不做。</value>
         <value xml:lang="zh-TW">沒有指定的檔案名/網址或完整的XML檔,什麼也不做.</value>
     </property>
+    <property key="EntityImportNoJsonFileSpecified">
+        <value xml:lang="en">No filename/URL or complete JSON document specified, doing nothing.</value>
+        <value xml:lang="fr">Pas de fichier, URL ou flux JSON fournit, aucune action réalisée.</value>
+        <value xml:lang="it">Nessun nome file/URL o documento JSON completo specificato, nientre verrà fatto.</value>
+        <value xml:lang="ja">ファイル名/URLまたは完全なJSON文書が指定されていません。処理を行いません。</value>
+        <value xml:lang="vi">Không Tập tin/URL hoặc tài liệu JSON hoàn chỉnh nào được xác định nên hệ thống sẽ không làm gì cả.</value>
+        <value xml:lang="zh">没有指定的文件名/网址或完整的JSON文档,什么也不做。</value>
+        <value xml:lang="zh-TW">沒有指定的檔案名/網址或完整的JSON檔,什麼也不做.</value>
+    </property>
     <property key="EntityImportNumberOfEntityToBeProcessed">
         <value xml:lang="en">Got ${numberRead} entities from ${fileName}</value>
         <value xml:lang="fr">Chargement de ${numberRead} entités depuis ${fileName}</value>
@@ -116,6 +125,15 @@
         <value xml:lang="zh">解析实体XML文件时出错:${errorString}</value>
         <value xml:lang="zh-TW">解析資料實體XML檔時出錯:${errorString}</value>
     </property>
+    <property key="EntityImportJsonParsingError">
+        <value xml:lang="en">ERROR parsing Entity Json file: ${errorString}</value>
+        <value xml:lang="fr">ERREUR dans l'analyse de l'JSON : ${errorString}</value>
+        <value xml:lang="it">ERRORE di parsing file entità JSON: ${errorString}</value>
+        <value xml:lang="ja">エンティティJSONファイルパース時にエラー: ${errorString}</value>
+        <value xml:lang="vi">LỖI khi phân tích tập tin JSON Thá»±c thể: ${errorString}</value>
+        <value xml:lang="zh">解析实体JSON文件时出错:${errorString}</value>
+        <value xml:lang="zh-TW">解析資料實體JSON檔時出錯:${errorString}</value>
+    </property>
     <property key="EntityImportPassedFile">
         <value xml:lang="en">Pass ${passes} complete</value>
         <value xml:lang="fr">Passe ${passes} complete</value>
@@ -513,6 +531,32 @@
         <value xml:lang="zh">XML数据导出全部</value>
         <value xml:lang="zh-TW">XML資料匯出全部</value>
     </property>
+    <property key="PageTitleEntityExportJson">
+        <value xml:lang="de">JSON-Daten exportieren</value>
+        <value xml:lang="en">JSON Data Export</value>
+        <value xml:lang="fr">Export de données JSON</value>
+        <value xml:lang="it">Esporta dati in JSON</value>
+        <value xml:lang="ja">JSONデータエクスポート</value>
+        <value xml:lang="pt">Exportação de dados JSON</value>
+        <value xml:lang="ro">Exporta Date in JSON</value>
+        <value xml:lang="th">ส่งออกข้อมูล json</value>
+        <value xml:lang="vi">Kết xuất dữ liệu JSON</value>
+        <value xml:lang="zh">JSON数据导出</value>
+        <value xml:lang="zh-TW">JSON資料匯出</value>
+    </property>
+    <property key="PageTitleEntityExportAllJson">
+        <value xml:lang="de">Alle JSON-Daten exportieren</value>
+        <value xml:lang="en">JSON Data Export All</value>
+        <value xml:lang="fr">Exporter toutes les données JSON</value>
+        <value xml:lang="it">Esporta tutti i dati in JSON</value>
+        <value xml:lang="ja">JSONデータをすべてエクスポート</value>
+        <value xml:lang="pt">Exportação de todos os dados JSON</value>
+        <value xml:lang="ro">Exporta Toate Datele in JSON</value>
+        <value xml:lang="th">ส่งออกข้อมูล JSON ทั้งหมด</value>
+        <value xml:lang="vi">Kết xuất tất cả dữ liệu JSON</value>
+        <value xml:lang="zh">JSON数据导出全部</value>
+        <value xml:lang="zh-TW">JSON資料匯出全部</value>
+    </property>
     <property key="PageTitleEntityImport">
         <value xml:lang="de">XML-Daten importieren</value>
         <value xml:lang="en">XML Data Import</value>
@@ -539,6 +583,32 @@
         <value xml:lang="zh">XML数据导入目录 </value>
         <value xml:lang="zh-TW">XML資料匯入目錄 </value>
     </property>
+    <property key="PageTitleEntityImportJson">
+        <value xml:lang="de">JSON-Daten importieren</value>
+        <value xml:lang="en">JSON Data Import</value>
+        <value xml:lang="fr">Import de données JSON</value>
+        <value xml:lang="it">Importa dati da JSON</value>
+        <value xml:lang="ja">JSONデータインポート</value>
+        <value xml:lang="pt">Importar dados JSON</value>
+        <value xml:lang="ro">Importa Datele din JSON</value>
+        <value xml:lang="th">นำเข้าข้อมูล json</value>
+        <value xml:lang="vi">Nhập khẩu dữ liệu JSON</value>
+        <value xml:lang="zh">JSON数据导入</value>
+        <value xml:lang="zh-TW">JSON資料匯入</value>
+    </property>
+    <property key="PageTitleEntityImportDirJson">
+        <value xml:lang="de">JSON Datenimport aus Verzeichnis</value>
+        <value xml:lang="en">JSON Data Import Dir</value>
+        <value xml:lang="fr">Import de données JSON depuis un répertoire</value>
+        <value xml:lang="it">Importa dati JSON in directory</value>
+        <value xml:lang="ja">JSONデータインポートディレクトリ</value>
+        <value xml:lang="pt">Importar dados JSON em diretório</value>
+        <value xml:lang="ro">Importa Datele din JSON in Directory</value>
+        <value xml:lang="th">นำเข้าข้อมูล json ใน Directory</value>
+        <value xml:lang="vi">ThÆ° mục nhập khẩu JSON</value>
+        <value xml:lang="zh">JSON数据导入目录 </value>
+        <value xml:lang="zh-TW">JSON資料匯入目錄 </value>
+    </property>
     <property key="PageTitleEntityImportReaders">
         <value xml:lang="de">Leser für XML Datenimport</value>
         <value xml:lang="en">XML Data Import Readers</value>
@@ -1220,6 +1290,19 @@
         <value xml:lang="zh">完成XML文档 (根标签: entity-engine-xml)</value>
         <value xml:lang="zh-TW">完成XML檔 (根標簽: entity-engine-xml)</value>
     </property>
+    <property key="WebtoolsCompleteJsonData">
+        <value xml:lang="de">Vollständiges JSON dokument</value>
+        <value xml:lang="en">Complete JSON data</value>
+        <value xml:lang="fr">Compléter le data JSON</value>
+        <value xml:lang="it">Documento JSON</value>
+        <value xml:lang="ja">JSONドキュメントを完了する</value>
+        <value xml:lang="pt">Documento JSON completo</value>
+        <value xml:lang="ro">Completeaza documentul JSON</value>
+        <value xml:lang="th">เอกสาร JSON เสร็จเรียบร้อยแล้ว</value>
+        <value xml:lang="vi">Hoàn thành tài liệu JSON</value>
+        <value xml:lang="zh">完成JSON文档</value>
+        <value xml:lang="zh-TW">完成JSON檔</value>
+    </property>
     <property key="WebtoolsComponentsLoaded">
         <value xml:lang="de">Komponenten geladen</value>
         <value xml:lang="en">Loaded Components</value>
@@ -1997,6 +2080,18 @@
         <value xml:lang="zh">实体XML工具</value>
         <value xml:lang="zh-TW">資料實體XML工具</value>
     </property>
+    <property key="WebtoolsEntityJSONTools">
+        <value xml:lang="de">Entität JSON Tools</value>
+        <value xml:lang="en">Entity JSON Tools</value>
+        <value xml:lang="fr">Outils JSON d'entité</value>
+        <value xml:lang="it">Strumenti JSON per entità</value>
+        <value xml:lang="ja">エンティティJSONツール</value>
+        <value xml:lang="ro">Instrumente pentru Entitatea JSON</value>
+        <value xml:lang="th">เครื่องมือ Entity JSON </value>
+        <value xml:lang="vi">Công cụ JSON Thá»±c thể</value>
+        <value xml:lang="zh">实体JSON工具</value>
+        <value xml:lang="zh-TW">資料實體XML工具</value>
+    </property>
     <property key="WebtoolsEnvName">
         <value xml:lang="de">Umgebungsname</value>
         <value xml:lang="en">Env Name</value>
@@ -2186,6 +2281,19 @@
         <value xml:lang="zh">从数据源导出XML</value>
         <value xml:lang="zh-TW">從資料源匯出XML</value>
     </property>
+    <property key="WebtoolsExportJSONFromDataSource">
+        <value xml:lang="de">JSON Export aus der Datenquelle</value>
+        <value xml:lang="en">JSON Export from DataSource(s)</value>
+        <value xml:lang="fr">Export JSON depuis les sources de données</value>
+        <value xml:lang="it">Esportazione JSON da Datasource</value>
+        <value xml:lang="ja">データソースからJSONをエクスポート</value>
+        <value xml:lang="pt">Exportar de JSON DataSource(s)</value>
+        <value xml:lang="ro">Exporta Sursa Date in JSON</value>
+        <value xml:lang="th">JSON ที่มาจากแหล่งข้อมูล</value>
+        <value xml:lang="vi">Kết xuất JSON từ nguồn dữ liệu</value>
+        <value xml:lang="zh">从数据源导出JSON</value>
+        <value xml:lang="zh-TW">從資料源匯出JSON</value>
+    </property>
     <property key="WebtoolsExportable">
         <value xml:lang="de">Exportierbar</value>
         <value xml:lang="en">Exportable</value>
@@ -2640,6 +2748,19 @@
         <value xml:lang="zh">把XML导入到数据源</value>
         <value xml:lang="zh-TW">把XML匯入到資料源</value>
     </property>
+    <property key="WebtoolsJsonImportToDataSource">
+        <value xml:lang="de">JSON-Import in DataSource(n)</value>
+        <value xml:lang="en">JSON Import to DataSource(s)</value>
+        <value xml:lang="fr">Import JSON vers la source de données</value>
+        <value xml:lang="it">Importazione JSON in Datasource</value>
+        <value xml:lang="ja">データソースにJSONをインポート</value>
+        <value xml:lang="pt">Importar JSON para DataSource(s)</value>
+        <value xml:lang="ro">Importa Sursa de Date din JSON</value>
+        <value xml:lang="th">นำเข้า JSON ไปยังแหล่งข้อมูล</value>
+        <value xml:lang="vi">Nhập khẩu JSON từ nguồn dữ liệu</value>
+        <value xml:lang="zh">把JSON导入到数据源</value>
+        <value xml:lang="zh-TW">把JSON匯入到資料源</value>
+    </property>
     <property key="WebtoolsImportantLogLevel">
         <value xml:lang="de">Wichtig</value>
         <value xml:lang="en">Important</value>
@@ -5942,7 +6063,7 @@
     <property key="WebtoolsXMLExportInfo">
         <value xml:lang="de">Hier Seite können Daten aus der Datenbank exportiert werden. Die exportierten Dokumente werden in einem XML-root-tag "&lt;entity-engine-xml&gt;" enthalten sein. Es wird für jede Entität in dem für diese webapp konfigurierten Delegator eine eigene Datei erstellt.</value>
         <value xml:lang="en">This page can be used to export data from the database. The exported documents will have a root tag of "&lt;entity-engine-xml&gt;". There will be one file for each Entity in the configured delegator for this webapp.</value>
-        <value xml:lang="fr">Cette page peut être utilisée pour exporter des données depuis la base. Les documents exportés auront une balise de racine de "&lt;entity-engine-xml&gt;". Il y aura un dossier pour chaque entité dans le delegator configuré pour cette webapp.</value>
+        <value xml:lang="fr">Cette page peut être utilisée pour exporter des données depuis la base. Les documents exportés auront une balise de racine de "&lt;entity-engine-xml&gt;". Il y aura un fichier créé pour chaque entité dans le delegator configuré pour cette webapp.</value>
         <value xml:lang="it">Questa pagina può essere usata per esportare i dati dal database. I documenti esportati avranno un root tag "&lt;entity-engine-xml&gt;". C'è un file per ogni Entità nel delegator configurato per questa applicazione web.</value>
         <value xml:lang="ja">このページでデータベースからデータをエクスポートすることができます。エクスポートするドキュメントには"&lt;entity-engine-xml&gt;"ルートタグがあります。Webアプリケーションに設定されたデリゲータの中のそれぞれのエンティティに対して1つのファイルが作成されます。</value>
         <value xml:lang="pt">Esta página pode ser utilizado para exportar dados do banco de dados. Os documentos de exportação terão uma tag raiz de " &lt;entity-engine-xml&gt;". Haverá um arquivo para cada entidade do delegator configurado para essa webapp.</value>
@@ -5952,6 +6073,43 @@
         <value xml:lang="zh">本页面用于从数据库导出数据 导出的文档会有一个"&lt;entity-engine-xml&gt;"的根标签在为这个应用配置的代表中,会对每个实体有一个文件。</value>
         <value xml:lang="zh-TW">本頁面用於從資料庫匯出資料 匯出的檔會有一個"&lt;entity-engine-xml&gt;"的根標簽在為這個應用配置的代表中,會對每個資料實體有一個檔.&lt;/entity-engine-xml&gt;</value>
     </property>
+    <property key="WebtoolsWroteNRecordsToJSONFile">
+        <value xml:lang="de">Es wurden ${numberWritten} Datensätze in die Datei ${parameters.filename} geschrieben.</value>
+        <value xml:lang="en">Wrote ${numberWritten} records to JSON file ${parameters.filename}.</value>
+        <value xml:lang="fr">Ecrire ${numberWritten} les enregistrements dans le fichier JSON ${parameters.filename}.</value>
+        <value xml:lang="it">Scritti ${numberWritten} records su file JSON ${parameters.filename}.</value>
+        <value xml:lang="ja">${numberWritten} レコードを、JSONファイル ${parameters.filename} に書き出しました。</value>
+        <value xml:lang="pt">Escreveu  ${numberWritten} registros para o arquivo JSON ${parameters.filename}.</value>
+        <value xml:lang="th">เขียน ${numberWritten} บันทึกมูล XML ไฟล์ ${parameters.filename}.</value>
+        <value xml:lang="vi">Đã ghi ${numberWritten} bản ghi tới tập tin JSON ${parameters.filename}.</value>
+        <value xml:lang="zh">把${numberWritten}个记录写到JSON文件${parameters.filename}。</value>
+        <value xml:lang="zh-TW">把${numberWritten}個記錄寫到JSON檔${parameters.filename}.</value>
+    </property>
+    <property key="WebtoolsWroteJSONForAllDataIn">
+        <value xml:lang="de">Es wurde JSON für alle Daten in ${numberofEntities} Entitäten geschrieben.</value>
+        <value xml:lang="en">Wrote JSON for all data in ${numberOfEntities} entities.</value>
+        <value xml:lang="fr">Ecrire JSON pour toute les donnée inclues ${numberOfEntities} Entités.</value>
+        <value xml:lang="it">Scritti JSON per tutti i dati in ${numberOfEntities} entità.</value>
+        <value xml:lang="ja">${numberOfEntities} エンティティのすべてのデータをXMLファイルに書き出しました。</value>
+        <value xml:lang="pt">Escreveu JSON para todos os dados em ${numberOfEntities} entidades.</value>
+        <value xml:lang="th">เขียน JSON สำหรับข้อมูลทั้งหมดใน ${numberOfEntities} entities.</value>
+        <value xml:lang="vi">Đã ghi JSON cho tất cả dữ liệu trong ${numberOfEntities} Thá»±c thể.</value>
+        <value xml:lang="zh">为${numberOfEntities}个实体中的全部数据写到JSON。</value>
+        <value xml:lang="zh-TW">為${numberOfEntities}個資料實體中的全部資料寫到JSON.</value>
+    </property>
+    <property key="WebtoolsJSONExportInfo">
+        <value xml:lang="de">Hier Seite können Daten aus der Datenbank exportiert werden. Es wird für jede Entität in dem für diese webapp konfigurierten Delegator eine eigene Datei erstellt.</value>
+        <value xml:lang="en">This page can be used to export data from the database. There will be one file for each Entity in the configured delegator for this webapp.</value>
+        <value xml:lang="fr">Cette page peut être utilisée pour exporter des données depuis la base. Il y aura un fichier créé pour chaque entité dans le delegator configuré pour cette webapp.</value>
+        <value xml:lang="it">Questa pagina può essere usata per esportare i dati dal database. C'è un file per ogni Entità nel delegator configurato per questa applicazione web.</value>
+        <value xml:lang="ja">このページでデータベースからデータをエクスポートすることができます。Webアプリケーションに設定されたデリゲータの中のそれぞれのエンティティに対して1つのファイルが作成されます。</value>
+        <value xml:lang="pt">Esta página pode ser utilizado para exportar dados do banco de dados. Haverá um arquivo para cada entidade do delegator configurado para essa webapp.</value>
+        <value xml:lang="ro">Aceasta pagina poate fi folosita pentru exportarea datelor din  database. Exista un file pentru orice Entitate din delegatorul configurat pentru aceasta aplicatie web.</value>
+        <value xml:lang="th">หน้านี้ใช้การส่งออกข้อมูลจากฐานข้อมูลการส่ มี 1 ไฟล์สำหรับตัวอย่าง Entity ในการสร้างตัวแทนสำหรับโปรแกรมประยุกต์นี้</value>
+        <value xml:lang="vi">Giao diện bạn đang sá»­ dụng cung cấp chức năng Kết xuất dữ liệu từ CÆ¡ sở dữ liệu.( có 01 Tập tin với mỗi Thá»±c thể của Ứng dụng này)</value>
+        <value xml:lang="zh">本页面用于从数据库导出数据 的根标签在为这个应用配置的代表中,会对每个实体有一个文件。</value>
+        <value xml:lang="zh-TW">本頁面用於從資料庫匯出資料 的根標簽在為這個應用配置的代表中,會對每個資料實體有一個檔.</value>
+    </property>
     <property key="WebtoolsXMLImportInfo">
         <value xml:lang="de">Hier können zuvor exportierte Entitäten XML Dateien wieder in die Datenbank importiert werden. Diese Dokumente haben eine XML-root-tag "&lt;entity-engine-xml&gt;".</value>
         <value xml:lang="en">This page can be used to import exported Entity Engine XML documents. These documents all have a root tag of "&lt;entity-engine-xml&gt;".</value>
@@ -5965,6 +6123,19 @@
         <value xml:lang="zh">本页面用于导入导出的实体引擎XML文档。这些文档都有一个根标签 "&lt;entity-engine-xml&gt;".</value>
         <value xml:lang="zh-TW">本頁面用於匯入匯出的資料實體引擎XML檔.這些檔都有一個根標簽 "&lt;entity-engine-xml&gt;".&lt;/entity-engine-xml&gt;</value>
     </property>
+    <property key="WebtoolsJSONImportInfo">
+        <value xml:lang="de">Hier können zuvor exportierte Entitäten JSON Dateien wieder in die Datenbank importiert werden.</value>
+        <value xml:lang="en">This page can be used to import exported Entity Engine JSON data.</value>
+        <value xml:lang="fr">Cette page peut être utilisée pour importer des documents JSON exportés par le moteur d'entité.".</value>
+        <value xml:lang="it">Questa pagina può essere usata per importare documenti JSON contenenti export di Entita'.</value>
+        <value xml:lang="ja">このページでエクスポートされたエンティティエンジンJSONドキュメントをインポートすることができます。</value>
+        <value xml:lang="pt">Esta página pode ser utilizado para importação e exportação de documentos JSON do Engine de Entidades.</value>
+        <value xml:lang="ro">Aceasta pagina nu poate fi folosita pentru importarea exportarea documentelor JSON de Entity Engine.</value>
+        <value xml:lang="th">หน้านี้ใช้การนำเข้าข้อมูลที่มีการส่งออกเอนติตี้เอนจินเอกสาร JSON โดยใช้คำสั่ง</value>
+        <value xml:lang="vi">Giao diện bạn đang sá»­ dụng cung cấp chức năng Nhập khẩu dữ liệu từ các Tài liệu Thá»±c thể JSON.</value>
+        <value xml:lang="zh">本页面用于导入导出的实体引擎JSON文档。</value>
+        <value xml:lang="zh-TW">本頁面用於匯入匯出的資料實體引擎JSON檔</value>
+    </property>
     <property key="WebtoolsYouMayCreateAnEntity">
         <value xml:lang="de">You may create an Entity ${entityName} by entering the values you want, and clicking Create.</value>
         <value xml:lang="en">You may create an Entity ${entityName} by entering the values you want, and clicking Create.</value>

Added: ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy?rev=1862206&view=auto
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy (added)
+++ ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy Thu Jun 27 11:49:22 2019
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.*
+import java.io.*
+import org.apache.ofbiz.base.util.*
+import org.apache.ofbiz.entity.model.*
+import org.apache.ofbiz.entity.util.*
+import org.apache.ofbiz.entity.transaction.*
+import org.apache.ofbiz.entity.condition.*
+
+outpath = parameters.outpath
+filename = parameters.filename
+maxRecStr = parameters.maxrecords
+entitySyncId = parameters.entitySyncId
+passedEntityNames = null
+if (parameters.entityName) passedEntityNames = parameters.entityName instanceof Collection ? parameters.entityName as TreeSet : [parameters.entityName] as TreeSet
+
+// get the max records per file setting and convert to a int
+maxRecordsPerFile = 0
+if (maxRecStr) {
+    try {
+        maxRecordsPerFile = Integer.parseInt(maxRecStr)
+    }
+    catch (Exception e) {
+    }
+}
+
+preConfiguredSetName = parameters.preConfiguredSetName
+if ("Product1".equals(preConfiguredSetName)) {
+    passedEntityNames = new LinkedHashSet()
+    passedEntityNames.add("DataResource")
+    passedEntityNames.add("Facility")
+    passedEntityNames.add("ProdCatalog")
+    passedEntityNames.add("Product")
+    passedEntityNames.add("ProductCategory")
+    passedEntityNames.add("ProductFeatureCategory")
+    passedEntityNames.add("ProductFeatureType")
+    passedEntityNames.add("ProductPriceRule")
+    passedEntityNames.add("ProductPromo")
+} else if ("Product2".equals(preConfiguredSetName)) {
+    passedEntityNames = new LinkedHashSet()
+    passedEntityNames.add("Content")
+    passedEntityNames.add("ElectronicText")
+    passedEntityNames.add("FacilityLocation")
+    passedEntityNames.add("ProdCatalogCategory")
+    passedEntityNames.add("ProdCatalogRole")
+    passedEntityNames.add("ProductAssoc")
+    passedEntityNames.add("ProductAttribute")
+    passedEntityNames.add("ProductCategoryMember")
+    passedEntityNames.add("ProductCategoryRollup")
+    passedEntityNames.add("ProductFacility")
+    passedEntityNames.add("ProductFeature")
+    passedEntityNames.add("ProductFeatureCategoryAppl")
+    passedEntityNames.add("ProductKeyword")
+    passedEntityNames.add("ProductPrice")
+    passedEntityNames.add("ProductPriceAction")
+    passedEntityNames.add("ProductPriceCond")
+    passedEntityNames.add("ProductPromoCode")
+    passedEntityNames.add("ProductPromoCategory")
+    passedEntityNames.add("ProductPromoProduct")
+    passedEntityNames.add("ProductPromoRule")
+} else if ("Product3".equals(preConfiguredSetName)) {
+    passedEntityNames = new LinkedHashSet()
+    passedEntityNames.add("ProdCatalogInvFacility")
+    passedEntityNames.add("ProductContent")
+    passedEntityNames.add("ProductFacilityLocation")
+    passedEntityNames.add("ProductFeatureAppl")
+    passedEntityNames.add("ProductFeatureDataResource")
+    passedEntityNames.add("ProductFeatureGroup")
+    passedEntityNames.add("ProductPriceChange")
+    passedEntityNames.add("ProductPromoAction")
+    passedEntityNames.add("ProductPromoCodeEmail")
+    passedEntityNames.add("ProductPromoCodeParty")
+    passedEntityNames.add("ProductPromoCond")
+} else if ("Product4".equals(preConfiguredSetName)) {
+    passedEntityNames = new LinkedHashSet()
+    passedEntityNames.add("InventoryItem")
+    passedEntityNames.add("ProductFeatureCatGrpAppl")
+    passedEntityNames.add("ProductFeatureGroupAppl")
+} else if ("CatalogExport".equals(preConfiguredSetName)) {
+    passedEntityNames = new LinkedHashSet()
+    passedEntityNames.add("ProdCatalogCategoryType")
+    passedEntityNames.add("ProdCatalog")
+    passedEntityNames.add("ProductCategoryType")
+    passedEntityNames.add("ProductCategory")
+    passedEntityNames.add("ProductCategoryRollup")
+    passedEntityNames.add("ProdCatalogCategory")
+    passedEntityNames.add("ProductFeatureType")
+    passedEntityNames.add("ProductFeatureCategory")
+
+    passedEntityNames.add("DataResource")
+    passedEntityNames.add("Content")
+    passedEntityNames.add("ElectronicText")
+
+    passedEntityNames.add("ProductType")
+    passedEntityNames.add("Product")
+    passedEntityNames.add("ProductAttribute")
+    passedEntityNames.add("GoodIdentificationType")
+    passedEntityNames.add("GoodIdentification")
+    passedEntityNames.add("ProductPriceType")
+    passedEntityNames.add("ProductPrice")
+
+    passedEntityNames.add("ProductPriceRule")
+    passedEntityNames.add("ProductPriceCond")
+    passedEntityNames.add("ProductPriceAction")
+    //passedEntityNames.add("ProductPriceChange")
+
+    passedEntityNames.add("ProductPromo")
+    passedEntityNames.add("ProductPromoCode")
+    passedEntityNames.add("ProductPromoCategory")
+    passedEntityNames.add("ProductPromoProduct")
+    passedEntityNames.add("ProductPromoRule")
+    passedEntityNames.add("ProductPromoAction")
+    passedEntityNames.add("ProductPromoCodeEmail")
+    passedEntityNames.add("ProductPromoCodeParty")
+    passedEntityNames.add("ProductPromoCond")
+
+    passedEntityNames.add("ProductCategoryMember")
+    passedEntityNames.add("ProductAssoc")
+    passedEntityNames.add("ProductContent")
+
+    passedEntityNames.add("ProductFeature")
+    passedEntityNames.add("ProductFeatureCategoryAppl")
+    passedEntityNames.add("ProductFeatureAppl")
+    passedEntityNames.add("ProductFeatureDataResource")
+    passedEntityNames.add("ProductFeatureGroup")
+    passedEntityNames.add("ProductFeatureCatGrpAppl")
+    passedEntityNames.add("ProductFeatureGroupAppl")
+
+    //passedEntityNames.add("ProductKeyword")
+}
+
+if (entitySyncId) {
+    passedEntityNames = org.apache.ofbiz.entityext.synchronization.EntitySyncContext.getEntitySyncModelNamesToUse(dispatcher, entitySyncId)
+}
+checkAll = "true".equals(parameters.checkAll)
+tobrowser = parameters.tobrowser != null
+context.tobrowser = tobrowser
+
+entityFromCond = null
+entityThruCond = null
+entityDateCond = null
+if (entityFrom) {
+    entityFromCond = EntityCondition.makeCondition("lastUpdatedTxStamp", EntityComparisonOperator.GREATER_THAN, entityFrom)
+}
+if (entityThru) {
+    entityThruCond = EntityCondition.makeCondition("lastUpdatedTxStamp", EntityComparisonOperator.LESS_THAN, entityThru)
+}
+if (entityFromCond && entityThruCond) {
+    entityDateCond = EntityCondition.makeCondition(entityFromCond, EntityJoinOperator.AND, entityThruCond)
+} else if (entityFromCond) {
+    entityDateCond = entityFromCond
+} else if (entityThruCond) {
+    entityDateCond = entityThruCond
+}
+
+reader = delegator.getModelReader()
+modelEntities = reader.getEntityCache().values() as TreeSet
+context.modelEntities = modelEntities
+
+if (passedEntityNames) {
+    if (tobrowser) {
+        session.setAttribute("jsonrawdump_entitylist", passedEntityNames)
+        session.setAttribute("entityDateCond", entityDateCond)
+    } else {
+        efo = new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true)
+        numberOfEntities = passedEntityNames?.size() ?: 0
+        context.numberOfEntities = numberOfEntities
+        numberWritten = 0
+        currentIndexEntity = 0
+
+        // single file
+        if (filename && numberOfEntities) {
+            if (outpath && !(filename.contains("/") && filename.contains("\\"))) {
+                filename = outpath + File.separator + filename;
+            }
+            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), "UTF-8")))
+            writer.println('[')
+
+            passedEntityNames.each { curEntityName ->
+                if (entityFrom) {
+                    curModelEntity = reader.getModelEntity(curEntityName)
+                    if (curModelEntity instanceof ModelViewEntity) {
+                        return
+                    }
+                }
+                currentIndexEntity++;
+                beganTransaction = TransactionUtil.begin(3600)
+                try {
+                    me = reader.getModelEntity(curEntityName)
+                    if (me.getNoAutoStamp() || me instanceof ModelViewEntity) {
+                        values = delegator.find(curEntityName, null, null, null, me.getPkFieldNames(), efo)
+                    } else {
+                        values = delegator.find(curEntityName, entityDateCond, null, null, UtilMisc.toList("-createdTxStamp"), efo)
+                    }
+
+                    curNumberWritten = 0
+                    writer.print('{');
+                    writer.print('"');
+                    writer.print(curEntityName);
+                    writer.print('"');
+                    writer.print(':');
+                    writer.print('[');
+                    while (value = values.next()) {
+                        org.apache.ofbiz.webtools.EntityJsonHelper.writeJsonText(writer, value)
+                        numberWritten++
+                        curNumberWritten++
+                        if (curNumberWritten % 500 == 0 || curNumberWritten == 1) {
+                            Debug.log("Records written [$curEntityName]: $curNumberWritten Total: $numberWritten")
+                        }
+                        if(curNumberWritten < values.getResultsSizeAfterPartialList()) {
+                            writer.print(",");
+                        }
+                    }
+                    writer.print("]}");
+                    values.close()
+                    Debug.log("Wrote [$curNumberWritten] from entity : $curEntityName")
+                    TransactionUtil.commit(beganTransaction)
+                } catch (Exception e) {
+                    errMsg = "Error reading data for JSON export:"
+                    Debug.logError(e, errMsg, "JSP")
+                    TransactionUtil.rollback(beganTransaction, errMsg, e)
+                }
+                if(currentIndexEntity < numberOfEntities) {
+                    writer.println(',');
+                } else {
+                    writer.println('');
+                }
+            }
+            writer.print(']')
+            writer.close()
+            Debug.log("Total records written from all entities: $numberWritten")
+            context.numberWritten = numberWritten
+        }
+
+        // multiple files in a directory
+        results = []
+        fileNumber = 1
+        context.results = results
+        if (outpath && !filename) {
+            outdir = new File(outpath)
+            if (!outdir.exists()) {
+                outdir.mkdir()
+            }
+            if (outdir.isDirectory() && outdir.canWrite()) {
+                passedEntityNames.each { curEntityName ->
+                    numberWritten = 0
+                    fileName = preConfiguredSetName ? UtilFormatOut.formatPaddedNumber((long) fileNumber, 3) + "_" : ""
+                    fileName = fileName + curEntityName
+
+                    values = null
+                    beganTransaction = false
+                    try {
+                        beganTransaction = TransactionUtil.begin(3600)
+
+                        me = delegator.getModelEntity(curEntityName)
+                        if (me instanceof ModelViewEntity) {
+                            results.add("[$fileNumber] [vvv] $curEntityName skipping view entity")
+                            return
+                        }
+                        if (me.getNoAutoStamp() || me instanceof ModelViewEntity) {
+                            values = delegator.find(curEntityName, null, null, null, me.getPkFieldNames(), efo)
+                        } else {
+                            values = delegator.find(curEntityName, entityDateCond, null, null, me.getPkFieldNames(), efo)
+                        }
+                        isFirst = true
+                        writer = null
+                        fileSplitNumber = 1
+                        curValueCount = 0
+                        while (value = values.next()) {
+                            //Don't bother writing the file if there's nothing
+                            //to put into it
+                            if (isFirst) {
+                                writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outdir, fileName +".json")), "UTF-8")))
+                                writer.println('[');
+                                writer.print('{');
+                                writer.print('"');
+                                writer.print(curEntityName);
+                                writer.print('"');
+                                writer.print(':');
+                                writer.print('[')
+                                isFirst = false
+                            }
+                            org.apache.ofbiz.webtools.EntityJsonHelper.writeJsonText(writer, value)
+                            numberWritten++
+                            curValueCount++
+                            if(curValueCount < values.getResultsSizeAfterPartialList()) {
+                                writer.println(',')
+                            }
+
+                            // split into small files
+                            if (maxRecordsPerFile > 0 && (numberWritten % maxRecordsPerFile == 0)) {
+                                fileSplitNumber++
+                                // close the file
+                                writer.println("]}")
+                                writer.print("]")
+                                writer.close()
+
+                                // create a new file
+                                splitNumStr = UtilFormatOut.formatPaddedNumber((long) fileSplitNumber, 3)
+                                writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outdir, fileName + "_" + splitNumStr +".json")), "UTF-8")))
+                                writer.println('[');
+                                writer.print('{');
+                                writer.print('"');
+                                writer.print(curEntityName);
+                                writer.print('"');
+                                writer.print(':');
+                                writer.print('[');
+                            }
+
+                            if (numberWritten % 500 == 0 || numberWritten == 1) {
+                                Debug.log("Records written [$curEntityName]: $numberWritten")
+                            }
+
+                        }
+                        if (writer) {
+                            writer.println("]}")
+                            writer.print("]")
+                            writer.close()
+                            String thisResult = "[$fileNumber] [$numberWritten] $curEntityName wrote $numberWritten records"
+                            Debug.log(thisResult)
+                            results.add(thisResult)
+                        } else {
+                            thisResult = "[$fileNumber] [---] $curEntityName has no records, not writing file"
+                            Debug.log(thisResult)
+                            results.add(thisResult)
+                        }
+                        values.close()
+                    } catch (Exception ex) {
+                        if (values != null) {
+                            values.close()
+                        }
+                        thisResult = "[$fileNumber] [xxx] Error when writing $curEntityName: $ex"
+                        Debug.log(thisResult)
+                        results.add(thisResult)
+                        TransactionUtil.rollback(beganTransaction, thisResult, ex)
+                    } finally {
+                        // only commit the transaction if we started one... this will throw an exception if it fails
+                        TransactionUtil.commit(beganTransaction)
+                    }
+                    fileNumber++
+                }
+            }
+        }
+    }
+} else {
+    context.numberOfEntities = 0
+}

Propchange: ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/ofbiz-framework/trunk/framework/webtools/groovyScripts/entity/JsonDsDump.groovy
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: ofbiz/ofbiz-framework/trunk/framework/webtools/servicedef/services.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webtools/servicedef/services.xml?rev=1862206&r1=1862205&r2=1862206&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webtools/servicedef/services.xml (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webtools/servicedef/services.xml Thu Jun 27 11:49:22 2019
@@ -56,6 +56,36 @@ under the License.
         <attribute name="placeholderValues" type="java.util.Map" mode="IN" optional="true"/>
         <attribute name="messages" type="List" mode="OUT" optional="false"/>
     </service>
+    <service name="parseEntityJsonFile" engine="java"
+             location="org.apache.ofbiz.webtools.WebToolsServices" invoke="parseEntityJsonFile" auth="true" use-transaction="false">
+        <description>Parses an entity xml file or an entity xml text</description>
+        <permission-service service-name="entityMaintPermCheck" main-action="VIEW"/>
+        <attribute name="url" type="java.net.URL" mode="IN" optional="true"/>
+        <attribute name="xmltext" type="String" mode="IN" optional="true" allow-html="any"/>
+        <attribute name="onlyInserts" type="String" mode="IN" optional="true"/>
+        <attribute name="maintainTimeStamps" type="String" mode="IN" optional="true"/>
+        <attribute name="txTimeout" type="Integer" mode="IN" optional="true"/>
+        <attribute name="createDummyFks" type="String" mode="IN" optional="true"/>
+        <attribute name="checkDataOnly" type="String" mode="IN" optional="true"/>
+        <attribute name="placeholderValues" type="java.util.Map" mode="IN" optional="true"/>
+        <attribute name="rowProcessed" type="Long" mode="OUT" optional="false"/>
+    </service>
+    <service name="entityImportJson" engine="java"
+             location="org.apache.ofbiz.webtools.WebToolsServices" invoke="entityImportJson" auth="true" use-transaction="false">
+        <description>Imports an entity xml file or text string</description>
+        <permission-service service-name="entityMaintPermCheck" main-action="VIEW"/>
+        <attribute name="filename" type="String" mode="IN" optional="true" allow-html="any"/>
+        <attribute name="fmfilename" type="String" mode="IN" optional="true" allow-html="any"/>
+        <attribute name="fulltext" type="String" mode="IN" optional="true" allow-html="any"/>
+        <attribute name="isUrl" type="String" mode="IN" optional="true"/>
+        <attribute name="onlyInserts" type="String" mode="IN" optional="true"/>
+        <attribute name="maintainTimeStamps" type="String" mode="IN" optional="true"/>
+        <attribute name="createDummyFks" type="String" mode="IN" optional="true"/>
+        <attribute name="checkDataOnly" type="String" mode="IN" optional="true"/>
+        <attribute name="txTimeout" type="Integer" mode="IN" optional="true"/>
+        <attribute name="placeholderValues" type="java.util.Map" mode="IN" optional="true"/>
+        <attribute name="messages" type="List" mode="OUT" optional="false"/>
+    </service>
     <service name="entityImportDir" engine="java"
             location="org.apache.ofbiz.webtools.WebToolsServices" invoke="entityImportDir" auth="true" use-transaction="false">
         <description>Imports all entity xml files contained in a directory</description>
@@ -71,6 +101,21 @@ under the License.
         <attribute name="placeholderValues" type="java.util.Map" mode="IN" optional="true"/>
         <attribute name="messages" type="List" mode="OUT" optional="false"/>
     </service>
+    <service name="entityImportDirJson" engine="java"
+             location="org.apache.ofbiz.webtools.WebToolsServices" invoke="entityImportDirJson" auth="true" use-transaction="false">
+        <description>Imports all entity json files contained in a directory</description>
+        <permission-service service-name="entityMaintPermCheck" main-action="VIEW"/>
+        <attribute name="path" type="String" mode="IN" optional="true"/>
+        <attribute name="onlyInserts" type="String" mode="IN" optional="true"/>
+        <attribute name="maintainTimeStamps" type="String" mode="IN" optional="true"/>
+        <attribute name="createDummyFks" type="String" mode="IN" optional="true"/>
+        <attribute name="checkDataOnly" type="String" mode="IN" optional="true"/>
+        <attribute name="deleteFiles" type="String" mode="IN" optional="true"/>
+        <attribute name="txTimeout" type="Integer" mode="IN" optional="true"/>
+        <attribute name="filePause" type="Long" mode="IN" optional="true"/>
+        <attribute name="placeholderValues" type="java.util.Map" mode="IN" optional="true"/>
+        <attribute name="messages" type="List" mode="OUT" optional="false"/>
+    </service>
     <service name="entityImportReaders" engine="java"
         location="org.apache.ofbiz.webtools.WebToolsServices" invoke="entityImportReaders" auth="true" use-transaction="false">
         <description>Imports an entity xml file or text string</description>
@@ -91,6 +136,15 @@ under the License.
         <permission-service service-name="entityMaintPermCheck" main-action="VIEW"/>
         <attribute name="outpath" type="String" mode="IN" optional="true"/>
         <attribute name="fromDate" type="Timestamp" mode="IN" optional="true"/>
+        <attribute name="txTimeout" type="Integer" mode="IN" optional="true"/>
+        <attribute name="results" type="List" mode="OUT" optional="false"/>
+    </service>
+    <service name="entityExportAllJson" engine="java"
+             location="org.apache.ofbiz.webtools.WebToolsServices" invoke="entityExportAllJson" auth="true" use-transaction="false">
+        <description>Exports all entities into json files</description>
+        <permission-service service-name="entityMaintPermCheck" main-action="VIEW"/>
+        <attribute name="outpath" type="String" mode="IN" optional="true"/>
+        <attribute name="fromDate" type="Timestamp" mode="IN" optional="true"/>
         <attribute name="txTimeout" type="Integer" mode="IN" optional="true"/>
         <attribute name="results" type="List" mode="OUT" optional="false"/>
     </service>